How to set preprocessor directives in makefile for kernel module build target? - makefile

I have a kernel module I'd like to build with any of make, make debug, make test, where the only difference between each one is a -D option to the compiler. This is essentially the same question as Creating a debug target in Linux 2.6 driver module makefile, but that one was marked as answered, and my question remains, after trying a few other things as well.
I've tried the deprecated EXTRA_CFLAGS option in my makefile:
debug:
$(MAKE) -C $(KDIR) M=$(PWD) EXTRA_CFLAGS="-DDEBUG" modules
as well as the newer ccflags-y option (doesn't seem to work even outside of the debug target):
ccflags-y := -DDEBUG
debug:
$(MAKE) -C $(KDIR) M=$(PWD) modules
and setting CFLAGS just before the debug target:
debug: CFLAGS_main.o=-DDEBUG
debug:
$(MAKE) -C $(KDIR) M=$(PWD) modules
but the only way I've found to accomplish what I want is with a separate build script:
#!/bin/sh
case "$1" in
debug)
make CFLAGS_main.o=-DDEBUG
;;
*)
make
;;
esac
Is there no way to do this directly in the makefile when building a kernel module??

You should be able to use your original version that used EXTRA_CFLAGS, but just replace EXTRA_CFLAGS with ccflags-y:
debug:
$(MAKE) -C $(KDIR) M=$(PWD) ccflags-y="-DDEBUG" modules
or replace it with CFLAGS_main.o to apply the CFLAGS to a single object:
debug:
$(MAKE) -C $(KDIR) M=$(PWD) CFLAGS_main.o="-DDEBUG" modules
EDIT
As mentioned by the OP Roger Dueck, setting variables on the make command line has a global effect. It overrides any setting of the same variables within the makefiles which may be undesirable, especially for a globally used variable such as ccflags-y. To avoid this, use your own makefile variable. In the "normal" part of the Makefile that invokes $(MAKE) on the "KBuild" part, change the debug: target to the following, using a custom variable of your choice (I used FOO_CFLAGS here):
debug:
$(MAKE) -C $(KDIR) M=$(PWD) FOO_CFLAGS="-DDEBUG" modules
In the "KBuild" part of the Makefile invoked by the above rule, use the following to append the custom CFLAGS from FOO_CFLAGS to ccflags-y:
ccflags-y += $(FOO_CFLAGS)

Related

How should I set extra include path for linux kernel module build?

This is the Makefile that I'm using for cross-buildig a kernel module.
export CROSS_COMPILE:=aarch64-none-linux-gnu-
export ARCH:=arm64
obj-m += chr_drv_ex1.o
export KDIR:=linux-source-5.4.0
#EXTRA_CFLAGS=-I../../qemu-5.1.0/hw/misc
#ccflags-y=-I../../qemu-5.1.0/hw/misc
all: test_chr_drv map_hugetlb test_ioctl_drv
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) modules
clean:
make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KDIR) M=$(PWD) clean
rm -f test_chr_drv map_hugetlb test_ioctl_drv
%: %.c
$(CROSS_COMPILE)gcc $^ -o $#
Now in my chr_drv_ex1.c file, I want to include a header file placed in ../../qemu-5.1.0/hw/misc directory. What is the correct method to add this path? I saw this and tried setting EXTRA_CFLAGS and ccflags-y but none of them works(those are commented out above). Of course if I use #include "../../qemu-5.1.0/hw/misc/axpu_regs.h" in the chr_drv_ex1.c source I can compile it. But I want to use #include <axpu_regs.h>.
I changed the ccflags-y from
ccflags-y := -I../../qemu-5.1.0/hw/misc
to
ccflags-y := -I../../../qemu-5.1.0/hw/misc
So the include path should be specified as seen from the kernel make directory (where the Makefile for linux kernel is located. in this case linux-source-5.4.0 directory which is one step below from where I am now).

How is obj-m variable exported to sub-make?

I am trying to learn linux kernel module building and kbuild by following https://www.tldp.org/LDP/lkmpg/2.6/lkmpg.pdf and reading GNU make manual.
Here is the Makefile of the first example, Hello-1, on The Linux Kernel Module Programming Guide:
obj-m += hello-1.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
AFAIK, obj-m should be read by kbuild. However according to GNU Make manual, I understand that obj-m shouldn't be exported.
Except by explicit request, make exports a variable only if it is
either defined in the environment initially or set on the command
line, and if its name consists only of letters, numbers, and
underscores. Some shells cannot cope with environment variable names
consisting of characters other than letters, numbers, and underscores.
https://www.gnu.org/software/make/manual/html_node/Variables_002fRecursion.html
obj-m is neither defined in the environment initially nor set on command line. So I expect that it shouldn't exported to recipe of target all. How does kbuild access obj-m?
Your makefile calls a submake and passes it the make variable M that points to the current directory. The submake is invoked with the -C option such that it is run as if it was invoked from the specified directory, that is the kernel source directory. It is thus the kernel build system with its own makefiles that is used. Thanks to the M variable the kernel makefiles know where they can find your makefile and include it with its obj-m definition.
Note: the makefile you show should probably be modified a bit with conditionals such that only the obj-m variable definition is visible from the Linux build system. Else, there is a risk of collision between the all and clean targets of your makefile and targets with the same names in the kernel makefiles. And, as noted by MadScientist, using make is not a good idea; $(MAKE) is preferable. You should probably use something like:
ifeq ($(KERNELRELEASE),)
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
else
obj-m += hello-1.o
endif

Is it possible to set CFLAGS to a linux kernel module Makefile?

Eg: a common device module's Makefile
obj-m:=jc.o
default:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules clean
I consider if I can set CFLAGS to the file. When I change default section to
$(MAKE) -O2 -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
But it didn't work.
Any help? Thanks a lot.
-O2 would be an option to make (or $(MAKE), as you're using it) in what you tried. Obviously, the compiler (probably gcc) needs this flag, not make.
Kbuild understands a make variable named CFLAGS_modulename.o to add specific C flags when compiling this unit. In your case, your module object will be jc.o, so you can specify:
CFLAGS_jc.o := -O2
and it should work. Add V=1 to your $(MAKE) lines to get a verbose output and you should see -O2 when jc.c is being compiled.
You can find more about compiling modules in the official documentation.
You can also use
ccflags-y := -O2
This will be applied to all of the source files compiled for your module with the Makefile. This is indirectly documented in the link provided by eepp in Section 4.2

GNU make unexpected behaviour

I want make to build all .cpp in the directory with tracking header changes. I attempt to do it by first making gcc output a target with dependencies with -MM option and then append the body to that target that will actually call the compilation:
OPTIONS = -std=c++11 -Wall
export
all : $(patsubst %.cpp, %.o, $(wildcard *.cpp))
%.o : %.mkt
make -f $*.mkt
%.mkt : %.cpp
gcc $(OPTIONS) -MM $*.cpp > $&.mkt1
echo gcc $(OPTIONS) -c %.cpp > $*.mkt2
cat $*.mkt1 $*.mkt2 > $*.mkt
Yet somehow this script issues the calls of the form
g++ -c -o something.o something.cpp
for each .cpp file in the directory. The temporary files .mkt1, .mkt2 and .mkt are not created. Why does this happen? How do i achive desired behaviour? I'm doing this on windows with mingw.
You have supplied a chain of two pattern rules (%.cpp->%.mkt, %.mkt->%.o), but Make already has a single implicit rule (%.cpp->%.o) which it will find first, when searching for a way to build something.o.
The simplest way to solve the problem is to use make -r (or make --no-builtin-rules) which will disable the built-in implicit rule.

some questions about GNU makefiles

i was wondering if anyone have sometime to answer some questions about GNU makefiles...
how to create a directory if it doesn't exists ("./obj") for output?
i have one makefile, but i got 2 build methods "Debug" and "Release", can i have both in 1 makefile and how to tell it which one to build?
ive been using Code::Blocks which builds only changed files, but my makefile builds them everytime i call make command, without touching any files. how can i make it build changed files only?
here is my current makefile
OBJPATH=./obj
COMPILER=gcc
Output: main.o Base64.o
$(COMPILER) -o Output.exe $(OBJPATH)/main.o $(OBJPATH)/Base64.o
strip Output.exe
main.o: main.c main.h
$(COMPILER) -c main.c -o $(OBJPATH)/main.o
Base64.o: Base64.c Base64.h
$(COMPILER) -c Base64.c -o $(OBJPATH)/Base64.o
thanks.
For the first question, you can put a fake target before any of the others, along the lines of:
preamble:
-mkdir obj
main.o: preamble main.c
blah blah blah
That will automatically execute everything in the preamble (you have to make it the first dependency in every rule) before it builds anything else. The - at the start of the mkdir ignores failures if, for example, the directory already exists.
For the second question, you can provide something like:
all: debug release
debug: blah blah blah
release: blah blah blah
and actually put the debug and release code in separate subdirectories. That way, you can build either with make release or make debug and build them both with make all.
Third question: Your makefile builds every time because the rules tell it to. For example, Output: main.o Base64.o will always try to build since Output never exists (the correct target seems to be Output.exe).
Similarly your object file rules will always execute since neither main.o nor Base64.o are updated by their statements (they update the files in the obj directory instead).
You may be able to fix that case by making the target $(OBJPATH)/main.o but, to be honest, I don't usually worry about separating objects and executables into separate directories. I tend to just lump them all into one directory and let make -clean clean them up.
So the makefile I would start with would be:
COMPILER=gcc
# Meta rules
all: release debug
release: Output.exe
debug: Output-d.exe
# Release stuff
Output.exe: main.o Base64.o
$(COMPILER) -o Output.exe main.o Base64.o
strip Output.exe
main.o: main.c main.h
$(COMPILER) -c main.c -o main.o
Base64.o: Base64.c Base64.h
$(COMPILER) -c Base64.c -o Base64.o
# Debug stuff
Output-d.exe: main-d.o Base64-d.o
$(COMPILER) -g -o Output-d.exe main-d.o Base64-d.o
main-d.o: main.c main.h
$(COMPILER) -g -DDEBUG -c main.c -o main-d.o
Base64-d.o: Base64.c Base64.h
$(COMPILER) -g -DDEBUG -c Base64.c -o Base64-d.o
In response to your comment question:
Is there anyway I can re-set a variable based on the Target Selected? for example if selected release OBJPATH will be "./obj/Release" if selected debug OBJPATH = "./obj/Debug"?
GNU Make may be more powerful than the ones I'm used to but you can do that by setting an environment variable then re-running make as per the following:
all: release debug
release:
( export zzvar=release ; $(MAKE) zz_Output.exe )
debug:
( export zzvar=debug ; $(MAKE) zz_Output-d.exe )
zz_Output.exe:
echo $(zzvar)
touch zz_Output.exe
zz_Output-d.exe: zz_main-d.o zz_Base64-d.o
echo $(zzvar)
touch zz_Output-d.exe
which outputs:
( export zzvar=release ; make zz_Output.exe )
make[1]: Entering directory '/home/pax'
echo release
release <==
touch zz_Output.exe
make[1]: Leaving directory '/home/pax'
( export zzvar=debug ; make zz_Output-d.exe )
make[1]: Entering directory '/home/pax'
echo debug
debug <==
touch zz_Output-d.exe
make[1]: Leaving directory '/home/pax'
You can see the two separate variables marked with <== above.
As I said, there's probably an easier way to do it with GNU Make but that'll get you started.
how to create a directory if it doesn't exists ("./obj") for output?
rm -Rf ./obj && mkdir ./obj
i have one makefile, but i got 2 build methods "Debug" and "Release", can i have both in 1 makefile and how to tell it which one to build?
You can have multiple top level build targets. Output in your makefile is a top level target. Make two. One called "Debug" and the other "Release". You can then say make Debug for the debug build and make Release for the release build.
ive been using Code::Blocks which builds only changed files, but my makefile builds them everytime i call make command, without touching any files. how can i make it build changed files only?
I haven't used Code::Blocks (I don't know what it is) but if your Makefiles are written properly (i.e. with dependencies properly specified), it will only rebuild the required targets.
In response to your comment question:
Is there anyway I can re-set a variable based on the Target Selected?
for example if selected release OBJPATH will be "./obj/Release" if
selected debug OBJPATH = "./obj/Debug"?
Here is how you do it without recursion:
COMPILER=gcc
release: OBJPATH = obj
release: Output.exe
debug: OBJPATH = obj-dbg
debug: Outputd.exe
Output%.exe: main.o Base64.o
$(COMPILER) -o $# $(OBJPATH)/main.o $(OBJPATH)/Base64.o
strip $#
main.o: main.c main.h
$(COMPILER) -c main.c -o $(OBJPATH)/main.o
Base64.o: Base64.c Base64.h
$(COMPILER) -c Base64.c -o $(OBJPATH)/Base64.o

Resources