Unknown symbol when compiling kernel module from multiple files - makefile

I am writing a kernel module that is comprised of several source files,
One of these source files has a function that needs to be used by the other objects in the same module.
It is defined in my file named ModemAPI.c
static void LogMessage ( char *format, ...)
This c file should be (together with other files) compiled into one kernel module, its makefile looks like this:
obj-m += ModemAPI.o
ModemAPI-objs := ../Common/StateMachine.o ../Common/ElementsPool.o
When I compile this kernel module, I get a warning during linking that the above function "LogMessage" is undefined and when I try to load the module I get an error saying it has an unknown symbol in it (of course LogMessage).
EDIT: Just to make clear, the function "LogMessage" is declared and implemented in the file ModemAPI.c, moreover it is exported via EXPORT_SYMBOL
EXPORT_SYMBOL(LogMessage);
In the files that use the function (such as StateMachine.c), it is declared via extern
extern void LogMessage ( char *format, ...);
The module compiles, the problem is in the linking stage.
Does anyone have any idea what could be the problem with this?
Thanks,
Roy.

In the Makefile try
obj-m += Module.o
Module-objs := ../Common/StateMachine.o ../Common/ElementsPool.o ../Common/ModemAPI.o
some times the order of the .o files are important

Answer for : moreover it is exported via EXPORT_SYMBOL
EXPORT_SYMBOL() makes LogMessage() is accessible to loadable kernel modules.
For eample, vmalloc() is exported to use in kernel modules http://lxr.free-electrons.com/source/mm/vmalloc.c#L1708
But still you have to include the vmalloc.h in loadable kernel module source.so do not confuse with extern and EXPORT_SYMBOL.
Solution
In your Makefile, modify as below
obj-m += ModemAPI.c StateMachine.c
i.e ModemAPI.c which has LogMessage() should be compiled first.
Change proposed to Makefile
obj-m += Mymodule.o
Mymodule-objs := ../Common/ModemAPI.o ../Common/StateMachine.o ../Common/ElementsPool.o
EDIT : 2
static in static void LogMessage ( char *format, ...) restricts the use of LogMessage() in other files. This concept is called static functions.

I believe the issue is that ModemAPI.c is not getting compiled.
obj-m += ModemAPI.o
ModemAPI-objs := ../Common/StateMachine.o ../Common/ElementsPool.o
Normally obj-m += ModemAPI.o tells make to use ModemAPI.c (I think that is the default for the linux make system), but adding the ModemAPI-objs tells make that ModemAPI.o is built using the objects ../Common/StateMachine.o ../Common/ElementsPool.o instead.
Try renaming the object or your ModemAPI.c file:
obj-m += modem.o #something not named ModemAPI.o
ModemAPI-objs := ModemAPI.o ../Common/StateMachine.o ../Common/ElementsPool.o
or
obj-m += ModemAPI.o
ModemAPI-objs := main.o ../Common/StateMachine.o ../Common/ElementsPool.o
where ModemAPI.c has been renamed to main.c

Make sure, that your kernel is compiled with support for module loading (e.g., if /proc/modules exists, you can be sure, it is)
Make sure, you are compiling against the same kernel, where you are trying to load the module
define obj-m += ... and [my-module-ko]-objs := [all xx.o object list]

Related

ccflag option in Makefile

I want to compile my c code (in kernel) which needs to include some header files from another directory.
Instead of specifying the complete path to header files in c file, I would like to specify the include path in Makefile.
My c file gets complied when the config option CONFIG_FEATURE_X is enabled.
I have written the following in Makefile:
obj-$(CONFIG_FEATURE_X) += my_file.o
ccflags-$(CONFIG_FEATURE_X) += -I$(obj)/../../path
When the CONFIG_FEATURE_X is enabled (Y) in .config using make menuconfig, it works fine.
But when the CONFIG_FEATURE_X is enabled as module (m) in .config, this does not include the header files from the path specified and gives the file not found error.
How can I do this?
When the CONFIG_FEATURE_X is enabled (Y) in .config using make menuconfig, it works fine.
That's because
ccflags-$(CONFIG_FEATURE_X) += -I$(obj)/../../path
would evaluate to
ccflags-y += -I$(obj)/../../path
According to Documentation/kbuild/makefiles.txt:
--- 3.7 Compilation flags
ccflags-y, asflags-y and ldflags-y
These three flags apply only to the kbuild makefile in which they
are assigned. They are used for all the normal cc, as and ld
invocations happening during a recursive build.
Note: Flags with the same behaviour were previously named:
EXTRA_CFLAGS, EXTRA_AFLAGS and EXTRA_LDFLAGS.
They are still supported but their usage is deprecated.
ccflags-y specifies options for compiling with $(CC).
So you have defined a valid compilation flag for the built-in case.
But when the CONFIG_FEATURE_X is enabled as module (m) in .config, this does not include the header files from the path specified and gives the file not found error.
That's because
ccflags-$(CONFIG_FEATURE_X) += -I$(obj)/../../path
would evaluate to
ccflags-m += -I$(obj)/../../path
According to the current version of Documentation/kbuild/makefiles.txt, there is no such compilation flag as "ccflags-m".
So the path specification is never used for the loadable module.
How can I do this?
Instead of the ccflags-$() flag, you could try to use CFLAGS_$#, a per-file options for $(CC).
CFLAGS_$#, AFLAGS_$#
CFLAGS_$# and AFLAGS_$# only apply to commands in current
kbuild makefile.
$(CFLAGS_$#) specifies per-file options for $(CC). The $#
part has a literal value which specifies the file that it is for.
Example:
# drivers/scsi/Makefile
CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF
CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ \
-DGDTH_STATISTICS
These two lines specify compilation flags for aha152x.o and gdth.o.
$(AFLAGS_$#) is a similar feature for source files in assembly
languages.
Example:
# arch/arm/kernel/Makefile
AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
So in your Makefile:
CFLAGS_my_file.o = -I$(obj)/../../path
As noted by the #sawdust answer, it seems (according to documentation) that only ccflags-y variable is supported, not a ccflags-m one.
However, for make things work you may use a "trick":
ccflags-y += ${ccflags-m}
Complete code:
obj-$(CONFIG_FEATURE_X) += my_file.o
ccflags-$(CONFIG_FEATURE_X) += -I$(obj)/../../path
# After all, add content of 'ccflags-m' variable to 'ccflags-y' one.
ccflags-y += ${ccflags-m}

Compile C file within C++ library in Makefile.am

I am forking an existing project on Github that I want to make some changes to. One thing I want to do is add an extra file. This file should be in one of the libraries that the Makefile.am generates. The problem is that the file I want to add is a .c file, while everything else in the project is .cpp.
The library that should contain the file is used like this in the makefile:
MYLIBRARY=path/mylibrary.a
...
path_mylibrary_a_CPPFLAGS = $(AM_CPPFLAGS)
path_mylibrary_a_CXXFLAGS = $(AM_CXXFLAGS)
path_mylibrary_a_SOURCES = \
path/cppfile1.cpp \
path/cppfile1.h \
path/cppfile2.cpp \
path/cppfile2.h \
path/cppfile3.cpp \
path/cppfile3.h
...
mybinary_LDADD = $(MYLIBRARY)
Simply adding the path/cfile.c and path/cfile.h to the list of sources gives me the following error:
CXXLD mybinary
/usr/bin/ld: path/mylibrary.a(path_mylibrary_a-cfile.o): relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
path/mylibrary.a: error adding symbols: Bad value
What can I do so the Makefile.am will compile the c file in a project that otherwise is built in c++?
The canonical way to solve this problem is to have a convenience library in which you compile your c code:
path_mylibrary_a_LDADD = cfile.la
noinst_LTLIBRARIES = cfile.la
cfile_la_CPPFLAGS = -std=c11
cfile_la_SOURCES = cfile.c
For more information refer to this answer.
I presume your cfile.h has the structure
#ifndef MYLIBRARY_CFILE_H
#define MYLIBRARY_CFILE_H
#ifdef __cplusplus
extern "C" {
#endif
/* your C declarations here */
#ifdef __cplusplus
}
#endif
#endif /* MYLIBRARY_CFILE_H */
Also, as a general tip: If you want to add a few source files to a library or program in a Makefile.am file, just add the lines
path_mylibrary_a_SOURCES += cfile.c cfile.h
which makes for a very clean patch which only adds one line, does not touch other lines, etc.
Also, I concur with https://stackoverflow.com/users/440558/some-programmer-dude on the path_mylibrary_a_CFLAGS which you probably need to add as well.

How to statically build kernel module with buildroot?

Is there an example package somewhere on how you might go about statically compiling in a device driver?
I know that obj-y is used for static compilation vs obj-m. I have a dynamically loadable module being built in my buildroot package right now. That dynamic module works exactly as I would expect. I even figured out that I could change the module makefile to use obj-y, and add a buildroot option where, if I clicked it it would append a line in the drivers/Makefile. The output appeared to show that my module got built. But it didn't at all seem to me that my driver's init function was being executed at startup, because I don't see my device file in /dev.
Supposing you have a driver in driver.c, and a buildroot package called STATICDRVR, you can use the following Config.in and STATICDRVR.mk files to add a static module to be built when the kernel is built:
Config.in
config BR2_PACKAGE_STATICDRVR
bool "Build & link static driver?"
help
This is a driver that blah blah greatness whatever
STATICDRVR.mk
STATICDRVR_VERSION = master
STATICDRVR_SITE = /location/to/STATICDRVR_containing_src
STATICDRVR_SITE_METHOD = local
STATICDRVR_MODULE_SUBDIRS = src
STATICDRVR_INSTALL_TARGET = YES
STATICDRVR_LICENSE = GPLv2
STATICDRVR_LICENSE_FILES = COPYING
STATICDRVR_NAME = STATICDRVR
STATICDRVR_DEPENDENCIES = linux
define STATICDRVR_BUILD_CMDS
#make sure that obj-y += STATICDRVR/ is only in the build makefile once
sed -i '/obj-y += STATICDRVR/d' $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/Makefile
echo "obj-y += STATICDRVR/" >> $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/Makefile
rm -rf $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/STATICDRVR
cp -r $(#D)/src $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/STATICDRVR
echo "obj-y += driver.o" > $(BUILD_DIR)/linux-$(LINUX_VERSION)/drivers/STATICDRVR/Makefile
endef
define STATICDRVR_INSTALL_STAGING_CMDS
endef
define STATICDRVR_INSTALL_TARGET_CMDS
endef
endif
define STATICDRVR_DEVICES
endef
define STATICDRVR_PERMISSIONS
endef
define STATICDRVR_USERS
endef
$(eval $(kernel-module))
$(eval $(generic-package))
It is not possible to statically link an external module with the kernel. To do that, you have to patch the kernel itself and add your module there.

How to add mutliple dendencies in Makefile

I have simple module program which will call to other function which are in other files. eg:
Arith.c <=== main module which calls sum(), diff(), mul() ...
|
|----sum.c
|----Sub.c
|----mul.c
|----div.c
|----remainder.c
|----quotient.c
I wrote the program for it. For make file I didn't understand how to write. I studied section 3.3 here and edited Makefile.
So now my make file is (I tested only for sum)
obj-m := arith.o
arith-y += sum.o
KDIR=/lib/modules/$(shell uname -r)/build
module:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean
When I run make it compiled and generated arith.ko. When I inserted it not showing anything in dmesg. When I inserted other modules dmesg working properly.
arith.c:
extern void sum(void);
int init_module(void)
{
pr_info("module inserted..\n");
sum();
return 0;
}
void cleanup_module(void)
{
pr_info("module removed..\n");
}
sum.c:
void sum(void)
{
pr_info("sum of 1 and 2: %d\n", 1+2);
}
Additional Info:
If I comment arith-y += sum.o in Makefile and not calling sum function in arith.c, dmesg working correctly.
Questions:
1.Is that make file correct?
2.If it is correct, why arith module not showing anything in dmesg even other modules working fine?
3.If it is not correct, how to write Makefile and why?
You never refer to obj-m or arith-y after declaring them; so they are either:
some sort of magic that your make knows about (which I doubt),
normal variables which you need to reference somewhere else, or
dead code.
You might have more luck looking through the kernel tree for a simple makefile which you can read and understand in detail.

:= or += when compiling Linux kernel modules?

Tutorials for compiling the kernel module for Linux, use different syntax for the Makefile.
Example 1
obj-m += rpi-pwm.o
Example 2
obj-m := nothing.o
What are the differences, and is there a preferred way?
:= would set the variable obj-m to nothing.o. This implies that if obj-m was previously set then it would be replaced by nothing.o.
+= would add rpi-pwm.o to the variable obj-m. If obj-m was previously set to nothing.o, then it'd become nothing.o rpi-pwm.o.
Saying
obj-m += rpi-pwm.o
is equivalent to saying
obj-m := $(obj-m) rpi-pwm.o

Resources