In-depth analysis of linux kernel module compilation makefile

Linux kernel loadable module makefile

When developing Linux kernel drivers, it is inevitable to come into contact with the writing and modification of makefiles. Although there are a lot of makefile templates on the Internet, you can use them in your own projects by making some simple modifications. However, for these basic things, it is more important to Know what is happening and why.

The Linux kernel uses the kbuild compilation system. When compiling loadable modules, the style of its makefile is different from the commonly used makefile for compiling C programs. However, the role of the makefile is always to provide compilation information to the compiler.

The simplest makefile

Let’s first take a look at what the simplest makefile looks like:

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

The function of this makefile is to compile the hello.c file and finally generate the hello.ko file.

obj-m + =hello.o
obj-m means compiling to generate a loadable module. Correspondingly, obj-y means directly compiling the module into the kernel. obj-m and obj-y are extended syntax provided by GNU make, not the universal syntax of gcc.

As you can see, the hello.c source file is not entered here. People who are familiar with makefiles should know that this is due to the automatic derivation function of the makefile. When you need to compile and generate the filename.o file without explicitly specifying the filename.c file location, make checks whether filename.c exists. If it exists, it compiles normally. If it does not exist, it reports an error.

obj-m + =hello.o, this statement explicitly compiles hello.o into hello.ko, and hello.o is generated by compiling the hello.c file through the automatic derivation function of make.

all, clean
All and clean are pseudo-targets in the makefile. The pseudo-target is not a real compilation target. It represents a series of command sets you want to execute. Usually a makefile will correspond to multiple operations, such as compilation and cleanup. The compilation results and installation can be marked using these pseudo-targets. When executing, you can type:

make clean
make install

Wait for instructions to complete the corresponding instruction operations. When make is followed without parameters, the operation of the first pseudo target is executed by default.

make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
The standard make command is as follows: make -C $KDIR M=$PWD [target]. The meaning of each field is introduced below.

  • -C option: This option specifies the location of the kernel source code. Make will enter the kernel source code directory when compiling, perform compilation, and return when the compilation is completed. Change to the directory dir before reading the Makefile or performing any other operations.
  • $KDIR:/lib/modules/$(shell uname -r)/build/, specifies the location of the kernel source code (in fact, the kernel source code is not required, what is needed is the kernel header file of the target machine).

When compiling directly on the target board, the kernel header file is stored in /lib/modules/$(shell uname -r)/build/ by default. This build/ directory is a soft link to the installation location of the source dock file. The real source code library of the kernel directly references the running kernel image.

From the above we can know that make -C is to go to the directory provided by /usr/src/linux-headers-5.13.0-52-generic to find the Makefile.
Then let us take a look at what is in the /usr/src/linux-headers-5.13.0-52-generic directory:

The files in this directory are kernel header files. Note that this is not the kernel source code. We also see the top-level Makefile in the kernel header file. What make actually executes is the top-level Makefile of the kernel header file.

Each directory under the kernel header file basically only has Kconfig and Makefile files:

We noticed that each kernel version has a kernel header file with -generic and without -generic. For the same version of the kernel, these two folders are only header files, not source code.

Directories with -generic contain symbolic links pointing to the contents of folders without -generic:

Note: Writing a kernel module does not require kernel source code, only kernel header files.
Download the kernel source code. Above we see that the /usr/src/ directory contains only kernel header files of different versions. We can download the kernel source code through the command: sudo apt-get install linux-source
I downloaded linux-source-5.4.0 here: sudo apt-get install linux-source-5.4.0

Delete source code:

sudo apt-get remove linux-source-5.4.0

When compiling cross-platform, you need to specify the corresponding kernel source code directory instead of the source code directory in the system. But when cross-compiling, do you need to specify the architecture platform and cross-compilation tool chain? Let’s look down;

  • M=$(PWD): The address of the module source file that needs to be compiled. The M= option causes the makefile to return to your module source directory before trying to build the module target.
  • [target]: modules, in fact, this is an optional option. The default behavior is to compile the source files and generate kernel modules, or module(s), but it also supports the following options:
  1. modules_install: Install this external module. The default installation address is /lib/modules/$(uname -r)/extra/. At the same time, the installation directory can be specified by the built-in variable INSTALL_MOD_PATH.
  2. Clean: Uninstall the files generated by the compilation process in the source file directory, which can be seen in the last line of the makefile above.
  3. help: help information

More options

Compile multiple source files

hello_world is always simple, but in actual development, more complex situations will occur. At this time, you need to know more makefile options:

First of all, when the generation of an .o target file depends on multiple source files, it is obvious that make’s automatic derivation rules are unable to do so (it can only deduce based on the same name, for example, when compiling filename.o, it will only look for filename.c). We This can be specified like this:

obj-m + = hello.o
    hello-y := a.o b.o hello_world.o

The hello.o target file depends on a.o, b.o, hello_world.o, so if a.o and b.o here do not specify source files, according to the derivation rules, they depend on the source files a.c, b.c, hello_world.c.
In addition to hello-y, hello-objs can also be used, and the effect is the same.

Compile multiple loadable modules simultaneously
kbuild supports compiling multiple loadable modules at the same time, that is, generating multiple .ko files. Its format is as follows:

obj-m := foo.o bar.o
    foo-y := <foo_srcs>
    bar-y := <bar_srcs>

It’s that simple.

ifneq ($(KERNELRELEASE),)
Usually, a standard makefile will be written like this:

ifneq ($(KERNELRELEASE),)
    obj-m := hello.o
else
    KDIR ?= /lib/modules/`uname -r`/build
 
    all:
            $(MAKE) -C $(KDIR) M=$(PWD) modules
    clean:
            $(MAKE) -C $(KDIR) M=$(PWD) clean
endif

Why add an ifneq, else, all conditional judgment.

This starts with the execution process of the Linux kernel module make: when typing make, make searches for the makefile in the current directory and executes it. KERNELRELEASE is defined in the top-level makefile, so KERNELRELEASE is not defined when the current makefile is executed. else branch, execute directly.

$(MAKE) -C $(KDIR) M=$(PWD) modules
This command will enter the $(KDIR) directory, call the top-level makefile, and define the KERNELRELEASE variable in the top-level makefile.

In the top-level makefile, the makefile in the current directory will be recursively called again. At this time, the KERNELRELEASE variable is no longer empty, so the if branch is executed and the hello module is added to the loadable module compilation list, thereby compiling the module into a loadable module. in the current directory.

In the final analysis, the function of the makefile files in subdirectories at all levels is to first switch to the top-level makefile, and then add the current module to the loadable module compilation list through obj-m, and kbuild will compile it into a loadable module.

If the entire kernel source code is compiled directly, the step of entering the top-level makefile in the else branch is omitted.

A basic concept to note is: for every compilation, the top-level makefile attempts to recursively enter each subdirectory to call the makefile of the subdirectory. However, when there are no modifications in the target subdirectory, repeated compilation will not be performed by default to save compilation time.

This also solves the above question: since compilation starts from the top directory, as long as the architecture (ARCH) and cross-compilation tool chain address (CROSS_COMPILE) are specified in the top directory, there is no need to specify these two in each subdirectory. parameter.

Placement of header files
When the compiled target module depends on multiple header files, kbuild has the following regulations for the placement of header files:

  • Place it directly in the same directory as the makefile. The current directory will be added to the header file search directory during compilation.
  • Place it in the system directory, which is include/linux/ in the source code directory.
  • Like the general makefile, use -I$(DIR) to specify. The difference is that the variable representing the compilation option is fixed and is ccflag.

The general usage is as follows: ccflags-y := -I$(DIR)/include kbuild will add the $(DIR)/include directory to the header file search directory during compilation.

Summary

Writing a kernel module does not require kernel source code, only the kernel header file (and corresponding Makefile).
If you want to develop the kernel, you need to modify the kernel source code.

Reference link: https://blog.csdn.net/m0_74282605/article/details/128114212

Reference link: https://blog.csdn.net/weixin_45030965/article/details/124413885

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. CS entry skill treeLinux introductionFirst introduction to Linux37607 people are learning the system