Need help in understanding Makefile for Kernel Module - linux-kernel

I am a newbie in Kernel Development. I was trying to understand the following makefile for Hello World! program. But I am not able to figure it out completely.
obj-m += hello.o
all:
sudo make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
sudo make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
I am not able to understand what is meant by 'obj-m += hello.o' . I know m here means module and thats it.
Also why are we not defining the dependencies of hello.o
And lastly I am not able to figure out completely the compiling rules defined under all: and clean:
Any help would be highly appreciated.!!

obj-m is a Makefile variable. It actually consists of 2 parts: 'obj' means that the referred target is a kernel object, while 'm' part means that the object is to be build like a module.
The variable is considered by kernel build rules. As kernel modules follow a certain convention, running your Makefile will result in creation of module hello.ko from source file hello.c (if everything works properly).
The 'obj' variable may take different suffixes as well. For example 'obj-y' will try to link the referred object into the main kernel image, instead of creating a module. The suffix may also refer to a kernel .config file variable, like this:
obj-$(CONFIG_HOTPLUG) += hotplug.o
In this case, if CONFIG_HOTPLUG is set to 'y' the hoplug object will be compiled into the main kernel; if set to 'm' then a separate hotplug.ko loadable module will be created. If not set to anything (resulting in 'obj-'), hotplug will be omitted outright.

Related

makefile target-specific changing srcs files

I'm trying to write a Makefile with a rule to make the project with another main.cpp file, because I'm testing my code with different options
I have different versions of the main function, that I put inside differents files : main.cpp, main_1.cpp, main_2.cpp, ..., to test different versions of my code, and they all have the same dependencies
first I was just commenting and un-commenting the Makefile variable MAIN that define the main.cpp file, but I was hoping there is a way to choose the one I want to try with a specific rule ?
I tried something with target-specific variables but it didn't work :
# # # # # # #
# VARIABLES #
# # # # # # #
NAME = my_program
VPATH = srcs
CXX = c++
CXXFLAGS = -I ./headers
OBJS = $(SRCS:%.cpp=%.o)
MAIN = main.cpp
#MAIN = main_1.cpp
SRCS = $(MAIN) file.cpp
# # # # #
# RULES #
# # # # #
all: $(NAME)
# target-specific variables
test-1: MAIN = main_1.cpp
test-1: re
$(NAME) : $(OBJS)
$(CXX) $(OBJS) -o $(NAME)
clean:
rm -f $(OBJS)
fclean: clean
rm -f $(NAME)
re: fclean all
.PHONY : all clean fclean re
the error output for main test_1 is :
c++ -I ./headers -c -o main.o srcs/main.cpp
c++ -I ./headers -c -o file.o srcs/file.cpp
c++ main_1.o Webserv.o -o my_program
c++: error: main_1.o: No such file or directory
Makefile:21: recipe for target 'my_program' failed
make: *** [my_program] Error 1
I think, then, that target-specific is not the right tool for what I'm trying to do.
Does Make provide a way to accomplish that (modifying the list of srcs files when calling a specific rule, and having the compilation working great with the new srcs files) ?
I'm vaguely thinking something like this.
test-%: main_%.cpp file.cpp
Now, make test-1 will produce an executable with that name from main_1.cpp instead of main.cpp, and similarly test-2 from main_2.cpp, etc.
If you have subsequent targets which hardcode my_program which should actually depend on which version you made, this might not be suitable, or at a minimum, you'd have to refactor those to use the current output executable. Similarly, you might want to add test-[1-9] to the files to remove in the clean target (or perhaps add a realclean target to remove them too).
Tangentially, several of your make variables don't seem to serve any immediate purpose. Putting stuff in variables makes sense for things you want to be able to override at compile time, or vaguely for making a general-purpose Makefile which can be applied with only minor modifications across several projects; but in isolation, these seem like unnecessary complexities you should probably avoid for the time being.
Your immediate problem could perhaps be solved by refactoring the dependency chain, but on the whole, I'd recommend keeping it as simple as possible. make already knows how to compile common source formats; all you really need to put in the Makefile are the dependencies which are not trivially obvious and any .PHONY targets, and overrides to select e.g. a specific default action.

How to add multiple Headers files path in a Makefile?

I am trying to compile uleds.c driver and this driver includes multiple files existing under this path :
/opt/poky-atmel/2.5.3/sysroots/cortexa5hf-neon-poky-linux-gnueabi/usr/src/kernel/include/linux
I want now to modify my Makefile and add this path so I can compile correctly uleds.c
This is my Makefile :
#CC=arm-poky-linux-gnueabi-gcc -march=armv7-a -marm -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a5 --sysroot=/opt/poky-atmel/2.5.3/sysroots/cortexa5hf-neon-poky-linux-gnueabi
#CC="gcc"
obj-m += uleds.o
KERNEL_SOURCE := /opt/poky-atmel/2.5.3/sysroots/cortexa5hf-neon-poky-linux-gnueabi/lib/modules/4.14.73-linux4sam-6.0-dirty
default:
${CC} ${KERNEL_SOURCE} uleds.c
clean:
${CC} $(INC) ${KERNEL_SOURCE} clean
Any suggestions for that ? Thank you
This appears to be an attempt at a kbuild file,.
You should not be manually compiling the file yourself using your default rule. Instead, you should be running the kernel's makefile, and have it compile the driver based on obj-m and friends.
Your makefile would look like so:
ifneq ($(KERNELRELEASE),)
ccflags-y += -I some/other/dir
obj-m += uleds.o
else
# default to build against running kernel if KDIR not
# specified:
KDIR ?= /lib/modules/`uname -r`/build
default:
$(MAKE) -C $(KDIR) M=$$PWD
endif
If you call make from the driver's directory, it will in turn call make from your kernel directory, which will know everything about the kernel and will be able to properly build your module.
Notice that by default, the built-in kernel's clean target will remove all generated *.[oas] files, so no need for a special clean target. Also, by default, the kernel's makefile will include its own include directories, so you likely don't need to do anything special for that. In case you do need to include from somewhere else, you can add a -I directive to the ccflags-y as shown in the example.
See Linux Kernel Makefiles and Building External Modules for details.
Simplest is:
${CC} -I/opt/poky-atmel/2.5.3/sysroots/cortexa5hf-neon-poky-linux-gnueabi/usr/src/kernel/include/linux uleds.c
Try reading the following to get familiar with other GCC (compiler) options: https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html#Directory-Options

Setting OS variable from rule

I am trying to build a shared library with one set of code, and everything works, except for this issue with my Makefile. Here's my (simplified) Makefile thus far:
OBJS = bar.o
libfoo.so: OS = LINUX # These don't seem to happen
libfoo.dll: OS = WINDOWS
# Linux
ifeq ($(OS), LINUX)
CC = gcc
...
# Windows
else ifeq ($(OS), WINDOWS)
CC = i686-pc-mingw32-gcc
...
endif
all: libfoo.so libfoo.dll
libfoo.so: clean $(OBJS)
...
libfoo.dll: clean $(OBJS)
...
bar.o: bar_$(OS).c bar.h
...
So, when you type make libfoo.so, I expect it to set OS = LINUX first. Then, when it gets to bar.o (it is a dependency of libfoo) it should know which bar_$(OS).c to use. However, I get the error:
make: *** No rule to make target `bar_.c', needed by bar.o. Stop.
Which tells me that when it tries to make bar.o, $(OS) is not set. But shouldn't that be the first thing that happens when I try to make libfoo.so, and that rule is evaluated?
Target-specific variables are available in the body of the rule, not in its prerequisites. But even if you could get this to work, you'd be asking for trouble: if you build one library and then the other, there's no way for Make to know that the bar.o that was made for the first is wrong for the second and should not be used.
There are several ways to get the effect you want, but none is perfect. I'd suggest using two different object file names, like bar_unix.o and bar_windows.o.
If you want to set a target-specific variable, and then have that variable available outside the body of that rule, you can recursively call the Makefile, after exporting the variable:
OBJS ?= foo.o # Use ? so it isn't blown away on recursive call
libfoo.so: OS = LINUX
libfoo.so: OBJS += linux_only.o
libfoo.so:
$(MAKE) -s build_libfoo_linux
build_libfoo_linux: $(OBJS)
#echo "OS = $(OS)" # Should print "OS = LINUX"
export OS # Can be anywhere
You have to remember to export the variables you want to "persist" after the recursive make call. And also, as shown above, if you append to any variables before the call, you'll want to make their initial assignment with ?= so they aren't set the second time.
You might want to detect the OS using uname and then conditionally compile. This explains

Invalid module format

$insmod helloworld module generates the error message "Invalid module format".
$dmesg outputs:
overflow in relocation type 10 val ffffffff88640070
'hello' likely not compiled with -mcmodel=kernel
The Makefile is a mix of tradition format (using (CC)) and module build system format "make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules".
The system is 1.6.18-194.el5 x86_64. The same Makefile works fine when it is used in i386 machine.
Any idea of what to look into? Thanks.
#Makefile, mix of (CC) and kernel module build system
CFLAGS+=-D"KBUILD_STR(s)=\#s" -D"KBUILD_BASENAME=KBUILD_STR(hello)"
CFLAGS+=-D__KERNEL__ -DMODULE -I$(KERNEL_BUILD_DIR)/include
KERNEL_BUILD_DIR=/lib/modules/2.6.18-194.el5/build
TARGETNAME=hello
BUILD_ALT_DIR=linux
# The main target (note that both library and driver are .ko files
#
all: $(BUILD_ALT_DIR)/$(TARGETNAME).ko
$(BUILD_ALT_DIR)/_$(TARGETNAME).o: hello.o
#echo Linking objects to loadable module
#mkdir -p $(BUILD_ALT_DIR)
#echo $(CURDIR)/$#
#$(LD) -Map=$#.map -r -o $# $^
#echo " LD_D [$#]"
$(BUILD_ALT_DIR)/$(TARGETNAME).ko: $(BUILD_ALT_DIR)/_$(TARGETNAME).o
#rm -f $(BUILD_ALT_DIR)/$(TARGETNAME).o
#echo create Makefile
#$(SHELL) -c 'echo "obj-m := $(TARGETNAME).o" > $(BUILD_ALT_DIR)/Makefile'
#$(SHELL) -c 'echo "$(TARGETNAME)-objs := _$(TARGETNAME).o" >> $(BUILD_ALT_DIR)/Makefile'
#$(SHELL) -c 'echo ".PHONY: `pwd`/$(BUILD_ALT_DIR)/_$(TARGETNAME).o" >> $(BUILD_ALT_DIR)/Makefile'
#$(SHELL) -c 'cd $(BUILD_ALT_DIR); $(MAKE) -C $(KERNEL_BUILD_DIR) M=`pwd`'
#echo " KO_D [$#]"
$(BUILD_ALT_DIR)/%.o: %.c
#echo Compiling C source to object file:
#mkdir -p $(BUILD_ALT_DIR)
# #echo $(CURDIR)/$#
#$(CC) -c -Wall $(CFLAGS) $(CFLAGS) $< -o $#
#echo " CC_D [$#]"
clean:
rm -f $(BUILD_ALT_DIR)/*.o $(BUILD_ALT_DIR)/*.d $(BUILD_ALT_DIR)/core $(BUILD_ALT_DIR)/*.map
hello.c
#include <linux/autoconf.h> // this is needed
#include <linux/init.h>
#include <linux/module.h>
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
The dmesg output tells you what is wrong:
'hello' likely not compiled with -mcmodel=kernel
On the x86-64 architecture, all code running in the kernel (including modules) must be compiled with a special flag, which tells the compiler to generate code which will run in the top half of the virtual address space (user mode programs run on the bottom half of the address space).
If I am reading your Makefile correctly, you are compiling the C code outside the kernel build system, and calling into the kernel build system only for the final linking. Do not do this. It is not just the memory model, there are several other flags which must be added when compiling the C source code. These flags can change with the kernel version, or even with the kernel configuration — you do not know, and you are not supposed to have to know, since the kernel build system deals with it all for you.
It is not just -mcmodel=kernel. There are many others, and getting them wrong can and will cause problems.
The fact that you are missing the correct flags is quite visible in your code:
#include <linux/autoconf.h> // this is needed
No, this is not needed. If you need it, you are doing it wrong. Take a look at one of the flags the kernel build system passes to the C compiler:
-include include/linux/autoconf.h
This flags tells the C compiler to always implicitly include the linux/autoconf.h header. Since it is always implicitly included, you never have to include it manually. And since you never include it manually, its location is allowed to change — it moved to generated/autoconf.h and later to linux/kconfig.h, and who knows where else it will end up next.
The fact that it worked at all for you in 32-bit x86 is just luck. You should do it the correct way even on 32-bit.
Second iteration:
All right, you've confirmed that Make builds _helloworld.o and $(BUILD_ALT_DIR)/Makefile. Now could you post the makefile that's in $(KERNEL_BUILD_DIR) (on the x86_64)?
(I'll go out on a limb and suggest that maybe $(BUILD_ALT_DIR) and $(KERNEL_BUILD_DIR) are the same on the i386 but not on the x84_64.)
EDIT: (third iteration)
1400 lines? Most of that in one branch? Half a dozen include directives? Architecture-specific branches? Autoconf?
Are you allowed to modify this nightmare in place, or must you tinker with the higher-level tools like automake? (I ask only because in the latter case it won't do much good to figure out what the problem is, you'll probably have to start over from scratch.)
1) From $(KERNEL_BUILD_DIR), try make M=$(BUILD_ALT_DIR) (whatever $(BUILD_ALT_DIR) is). Verify that that gives you the same error message. If it doesn't then ignore what follows (in this iteration).
2) From $(BUILD_ALT_DIR), try make, and see if it gives the same error. If it does, try replacing the makefile (in $(BUILD_ALT_DIR)) with a trivial makefile like
all:
#echo trivial makefile working
and test it, then try step 1 again. If it just says "trivial makefile working" and doesn't give the error, then things look good.
EDIT: (fourth iteration)
All right, we can't change $(KERNEL_BUILD_DIR)/Makefile. That doesn't necessarily sink us.
"Your suggestion (1) would not work, since $(BUILD_ALT_DIR) is a output directory, no source is in it."
Make does not require the presence of source code, and whether or not this would work (whatever "work" means), it is what your makefile appears to be attempting:
#$(SHELL) -c 'cd $(BUILD_ALT_DIR); $(MAKE) -C $(KERNEL_BUILD_DIR) M=`pwd`'
Try it and see what happens.
"Is there a possibility that x86_64 doesn't support tradition format (CC) for the kernel module?"
I'm not entirely sure what you mean, but we can probably answer your question by experiment.

Compiling with different flags in Makefile?

I have a single program used to interact with a joystick. It uses conditional compilation to specify a specific joystick. We do this right now by just hard coding the correct flag into the Makefile.
I'd like to make it so it uses a different flag based on the command given to the Makefile. So for example, I currently have this:
.PHONY: saitek
saitek: $(SOURCES)
$(COMPILE) -DSAITEK
.PHONY: logitech
logitech: $(SOURCES)
$(COMPILE) -DLOGITECH
I want only one of these commands to ever be run, and I want them all to make the same executable. But if I rerun 'make' it will compile the program again. I'd like it to recognize that it's already built the program.
Is there anyway to do this with a Makefile?
If you're using GNUMake, this will do what you're asking. It uses a different flag based on the command given to Make, and it doesn't rebuild the program unnecessarily.
.PHONY: saitek logitech
saitek: JOYSTICK=SAITEK
logitech: JOYSTICK=LOGITECH
# Suppose the actual name of your executable is "program"
saitek logitech: program
program: $(SOURCES)
$(COMPILE) -D$(JOYSTICK)
GNU make inherits variables from its environment, so if you specify
$ JOYSICK=LOGITECH
in your shell, and use
CFLAGS+=-D$(JOYSTICK)
in your makefile.
I question the necessity of this. You could just call make as something like make CFLAGS=-DSAITEK or use autoconf and substitute in the correct defines.
That said, how about something like this:
saitek logitech: program
.PHONY: saitek logitech
ifeq ($(MAKECMDGOALS),saitek)
CFLAGS += -DSAITEK
endif
ifeq ($(MAKECMDGOALS),logitech)
CFLAGS += -DLOGITECH
endif
program: $(OBJS)
# Whatever

Resources