I am learning assembly language and I create a simple linux kernel module to try how to call an assembly function from module, which in turns call a C function. The code compiles fine. However, when I try to insert the module, it causes kernel crash. I get the idea from this post: Calling C functions from x86 assembly language. I am wondering whether anybody can help me understand why it doesn't work.
The first is assembly code:
#include <linux/linkage.h>
ENTRY(sample_assembly_function)
pushq $10
call printMessage
add $0x4, %rsp
END(sample_assembly_function)
The second file is sample module file:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Proprietary");
void sample_assembly_function(void);
void printMessage(int num)
{
printk(KERN_ERR "MixAssemblyAndC: PrintMessage=%d.\n", num);
}
static int __init AModule_init(void)
{
sample_assembly_function();
return 0;
}
static void __exit AModule_exit(void)
{
printk("MixAssemblyAndC: Goodbye, world!\n");
}
module_init(AModule_init);
module_exit(AModule_exit);
And finally this is Makefile:
KERNELDIR:=/lib/modules/$(shell uname -r)/build
PWD=$(shell pwd)
obj-m += test.o
test-y := AModule.o ASample.o
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD)
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
When using the Linux 64-bit System V ABI calling convention you pass the first 6 integer class or pointer arguments in the registers RDI, RSI, RDX, RCX, R8, and R9. Any remaining parameters are pushed on the stack. The code you linked to in your question is 32-bit code and that uses the Linux i386 System V ABI as a calling convention. That is not compatible with 64-bit code.
Simply do mov $10, %rdi
to load the first parameter. Preferably you can do mov $10, %edi
. The latter moves the value 10 into EDI but since the target is a 32-bit register the processor automatically zero extends the value across the 64-bit register thus 10 is moved to RDI. The latter is a shorter encoding.
You need a ret
instruction to end your assembly language routine. The resulting code should look like:
#include <linux/linkage.h>
ENTRY(sample_assembly_function)
mov $10, %edi
call printMessage
ret
END(sample_assembly_function)
Since the call
is the last thing before ret
you can also just jmp printMessage
(Tail Call). When the ret
in printMessage
is reached, control will return to the function that called sample_assembly_function
. This code should work:
ENTRY(sample_assembly_function)
mov $10, %edi
jmp printMessage
END(sample_assembly_function)