How to write a Linux kernel module makefile? - linux

I'm trying to write a simple hello world kernel module. I'm working in Ubuntu 18.04.2 LTS on Virtual Box. In the directory /usr/src I created a directory named hello and inside that hello directory I've created hello.c and a makefile. Here is my makefile:
obj-m += hello.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
When I make the command "sudo make" I get this output:
make -C /lib/modules/5.3.0-28-generic/build M= modules
make[1]: Entering directory '/usr/src/linux-headers-5.3.0-28-generic'
make[2]: *** No rule to make target 'arch/x89/tools/relocs_32.c', needed by 'arch/x86/tools/relocs_32.o'. Stop.
arch/x86/Makefile:232: recipe for target 'archscripts' failed
make[1]: *** [archscripts] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.3.0-28-generic'
makefile:4: recipe for target 'all' failed
make: *** [all] Error 2
I've tried a few variations of my makefile including changing "PWD" to "shell pwd". I've installed all the essential tools and libraries as far as I know. What could be the problem here?

Variable PWD is set by the shell, so your Makefile relies on make invocation to be performed from the shell, as we normally do when type make in the terminal.
But sudo make executes make from a plain user environment. This environment lacks for PWD variable, so your Makefile behaves incorrectly. This can be found from your output:
make -C /lib/modules/5.3.0-28-generic/build M= modules
Use $(shell pwd) expression instead, it is more reliable:
obj-m += hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
Alternatively, you may call make with root privileges using this way:
sudo /bin/sh -c make
This way make is executed not from the plain root environment, but under an intermediate shell. This shell sets PWD variable.
Update
As noted by #Ian Abbott in the comments, another alternative for $(PWD) is $(CURDIR). Variable CURDIR is set by the make itself (precisely, by GNU Make). See also that question: bash: What is the difference between PWD and CURDIR?.

Your Makefile should look like this
obj-m += hello.o
Then your build command will look like
make -C $KDIR M=$PWD
Where $KDIR is the source tree of your kernel build.
is the way. The rest is handled by the kbuild system.
Please refer to refer to the official documentation for building off-tree modules

Related

WSL2&Linux make: make[1]: *** /lib/modules//build: No such file or directory. Stop

I am using the WSL2 and have already downloaded the Linux kernel to compile the module, however, when I enter make: it shows
code:
TARGETNAME = hello
OBJ = $(TARGETNAME).o
MODULE = $(TARGETNAME).ko
obj-m += $(OBJ)
all:
make -j $(nproc) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
install:
#modprobe -r $(TARGETNAME)
#install $(MODULE) /lib/modules/$(shell uname -r)/kernel/drivers/hid
#depmod
#modprobe $(TARGETNAME)
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Error:
tony#tony:~/OS/testa$ make
make -j -C /lib/modules/$(shell uname -r)/build
M=/home/tony/OS/testa modules
/bin/sh: 1: shell: Permission denied
make[1]: *** /lib/modules//build: No such file or directory.
Stop.
make: *** [Makefile:8: all] Error 2
And this is my wsl2(Linux file menu)
I am wondering that The reason for the error is that the executable program (make) in the target path cannot be found. And what should I do?

Predefine a macro in kernel module

I would like to define a macro for my kernel module by using the -D flag, but I can't figure out how to do it in a custom loadable kernel module.
Just to be clear, to set the macro TEST to 1 I usually do something like:
cc -D TEST=1 file.c -o file
And inside the file.c I have
#if TEST
//do something
#endif
Now, having the same code in a kernel module, how can I set TEST to 1 without touching the code?
This is my the Makefile:
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
Since the -C flag it's recursively calling multiple makefiles, adding -D TEST=1 does not work, I get the following error:
make: invalid option -- 'D'
Anybody knows how to solve this problem?
Thanks in advance.
As suggested by #n.m. in the comments, the solution is to use the EXTRA_CFLAGS. So in my case it would be something like this:
all:
make EXTRA_CFLAGS=-DTEST=2 -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
or simply
EXTRA_CFLAGS:= -D TEST=2
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

Makefile for kernel kecho command issue

This a makefile for compiling the kernel module.
# Makefile – makefile of our first driver
#
# if KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq (${KERNELRELEASE},)
obj-m = first-driver.o
# Otherwise we were called directly from the command line.
# Invoke the kernel build system.
else
KERNEL_SOURCE := /lib/modules/2.6.32-504.8.1.el6.x86_64/build
PWD := $(shell pwd)
default:
${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} modules
#echo kernel first-driver is ready
#$(kecho) 'Kernel: $# is ready'
clean:
${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} clean
endif
.PHONY : install remove
install :
sudo insmod first-driver.ko
remove :
sudo rmmod first-driver
Here I have used echo and kecho as per kernel makefile documantation but it giving the following error:
make -C /lib/modules/2.6.32-504.8.1.el6.x86_64/build SUBDIRS=/home/betatest/Device-Driver-Test modules
make[1]: Entering directory '/usr/src/kernels/2.6.32-504.8.1.el6.x86_64'
Building modules, stage 2.
MODPOST 1 modules
make[1]: Leaving directory '/usr/src/kernels/2.6.32-504.8.1.el6.x86_64'
kernel first-driver is ready
make: Kernel: default is ready: Command not found
Makefile:16: recipe for target 'default' failed
make: *** [default] Error 127
I am using GNU make version 4.1 and gcc version 4.4.7 where am I going wrong. Thanks.....
What is kecho variable's is assigned to? in the line #$(kecho) 'Kernel: $# is ready' kecho's value is null so the make considers only #$(kecho) 'Kernel: $# is ready' as a rule.
Did you forget to assign kecho to something at the beginning of the file? Like
kecho='echo'

Makefile:10: recipe for target 'modules' failed on multiple debian distros

FIXED
The problem was that I had spaces somewhere in the path to my source directory.
In this case, "Source Builds" had a space and screwed everything up.
Make sure you don't have any spaces anywhere in your folder names between root and your make directory.
So this error:
make[1]: ** No rule to make target 'Builds/ digimend-kernel-drivers-master'. Stop.
Came from the space in: ~/Source Builds/digimend-kernel-drivers-master
debian wheezy, jessie and now simplice 6 sid
I installed the build essentials package
the linux headers package for my kernel
ive tried on kernel 3.2.04, 3.16 and now 3.12
gcc version 4.9.2 (Debian 4.9.2-8)
GNU Make 4.0
this is what i get when i try to make.
willy#semplice:~/Source Builds/digimend-kernel-drivers-master$ make
make -C /lib/modules/3.12-7.semplice.0-desktop-686/build SUBDIRS=/home/willy/Source Builds/digimend-kernel-drivers-master modules
make[1]: Entering directory '/usr/src/linux-headers-3.12-7.semplice.0-desktop-686'
make[1]: *** No rule to make target 'Builds/digimend-kernel-drivers-master'. Stop.
make[1]: Leaving directory '/usr/src/linux-headers-3.12-7.semplice.0-desktop-686'
Makefile:10: recipe for target 'modules' failed
make: *** [modules] Error 2
Heres the makefile
ifneq ($(KERNELRELEASE),)
obj-m := hid-huion.o hid-uclogic.o
else
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
UDEV_RULES := /lib/udev/rules.d/70-hid-rebind.rules
DEPMOD_CONF := /etc/depmod.d/digimend.conf
HID_REBIND := /sbin/hid-rebind
modules modules_install clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) $#
install: modules_install
install -D -m 0644 digimend.conf $(DEPMOD_CONF)
depmod -a
install hid-rebind $(HID_REBIND)
install -m 0644 hid-rebind.rules $(UDEV_RULES)
udevadm control --reload
uninstall:
rm -vf $(UDEV_RULES) $(HID_REBIND) $(DEPMOD_CONF) \
/lib/modules/*/extra/hid-huion.ko \
/lib/modules/*/extra/hid-uclogic.ko
udevadm control --reload
depmod -a
endif
what is going on here... I am getting sad
FIXED
The problem was that I had spaces somewhere in the path to my source directory.
In this case, "Source Builds" had a space and screwed everything up.
Make sure you don't have any spaces anywhere in your folder names between root and your make directory.
So this error:
make[1]: ** No rule to make target 'Builds/ digimend-kernel-drivers-master'. Stop.
Came from the space in: ~/Source Builds/digimend-kernel-drivers-master
– user4369678

module compiling : asm/linkage.h file not found

I am trying to compile an example of "hello world" Kernel Module,
problems found on ubuntu 11.04, kernel 3.2.6, gcc 4.5.2 and fedora 16, kernel 3.2.7, gcc 4.6.7.
code:
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
static int __init hello_init (void)
{
printk("Hello module init\n");
return 0;
}
static void __exit hello_exit (void)
{
printk("Hello module exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
compiled with:
gcc -D__KERNEL__ -I /usr/src/linux/include/ -DMODULE -Wall -O2 -c hello.c -o hello.o
error:
In file included from /usr/src/linux/include/linux/kernel.h:13:0,
from /usr/src/linux/include/linux/cache.h:4,
from /usr/src/linux/include/linux/time.h:7,
from /usr/src/linux/include/linux/stat.h:60,
from /usr/src/linux/include/linux/module.h:10,
from hello.c:1: /usr/src/linux/include/linux/linkage.h:5:25: fatal error:
asm/linkage.h: file not found
then I found in /usr/src/linux/include/ there is no folder named 'asm' but 'asm-generic';
so I made a soft link 'asm' to 'asm-generic', and compiled agail:
this time the error was:
In file included from /usr/src/linux/include/linux/preempt.h:9:0,
from /usr/src/linux/include/linux/spinlock.h:50,
from /usr/src/linux/include/linux/seqlock.h:29,
from /usr/src/linux/include/linux/time.h:8,
from /usr/src/linux/include/linux/stat.h:60,
from /usr/src/linux/include/linux/module.h:10,
from hello.c:1: /usr/src/linux/include/linux/thread_info.h:53:29: fatal error:
asm/thread_info.h: file not found
So I realized I was wrong, but why ? T_T
obj-m += hello.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
is a proper way to build modules see kbuild documentation
And to see difference beetween your compiler invocation you could
cat /lib/modules/$(shell uname -r)/build/Makefile
And analyze an output
obj-m += hello.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
Here hello.c is your kernel source file. just use make to build your hello.ko module.
asm should be a link to the actual architecture you're compiling for, not to asm-generic.
You can't compile a generic kernel module, that would work on a generic architecture. You have to compile it for the particular architecture you're going to use.
I don't know why the asm didn't exist. It should be created as part of the configuration process.
You might get other errors later, if configuration is incomplete in other ways.
The asm includes (such as linkage.h) are architecture specific. There should be a set of directories under:
/usr/src/kernels/(kernel version goes here)/arch
that provide specific includes for the specific CPU architecture you are targeting your code to be compiled for.
Try adding this to your Makefile:
KVERSION :=R(shell uname -r)
and add the kernel and architecture (x86 in this example):
INCDIRS = -I./include -I/usr/src/kernels/$(KVERSION)/include -I/usr/src/kernels/$(KVERSION)/arch/x86
module compiling : asm/linkage.h file not found
This means this particular file was not found in specified DIR, which gets specified when we use -I option with make.
We can either link that asm-generic to asm, if all headers are present in asm-generic, or we can use make utility.
Make utility is preferred in case of building kernel modules.
Create a 'Makefile' in working DIR.
obj-m += hello.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
Use of -C option will change to DIR specified before reading the makefiles or doing anything else.
So to avoid this error, use -C option with DIR/lib/modules/$(shell uname -r)/build
By this your program will be able to find required files, you will get hello.ko file.
You can add this to kernel modules by
sudo insmod hello.ko
Similarly you can remove by
sudo rmmod hello

Resources