问题
$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);
回答1:
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.
回答2:
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.
来源:https://stackoverflow.com/questions/6471359/invalid-module-format