在 linux 内核中混合汇编和 C 函数 - x64 模式
Mix Assembly and C function in linux kernel - x64 mode
我正在学习汇编语言,我创建了一个简单的 linux 内核模块来尝试如何从模块调用汇编函数,该函数又调用 C 函数。代码编译得很好。但是,当我尝试插入模块时,它会导致内核崩溃。我从这个post得到了这个想法:Calling C functions from x86 assembly language。我想知道是否有人可以帮助我理解为什么它不起作用。
首先是汇编代码:
#include <linux/linkage.h>
ENTRY(sample_assembly_function)
pushq
call printMessage
add [=11=]x4, %rsp
END(sample_assembly_function)
第二个文件是示例模块文件:
#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);
最后这是 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
当使用 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 作为调用约定时。这与 64 位代码不兼容。
只需执行mov , %rdi
即可加载第一个参数。最好你可以做 mov , %edi
。后者将值 10 移动到 EDI,但由于目标是 32 位寄存器,处理器自动将值零扩展到 64 位寄存器,因此 10 移动到 RDI。后者是较短的编码。
您需要 ret
指令来结束您的汇编语言例程。生成的代码应如下所示:
#include <linux/linkage.h>
ENTRY(sample_assembly_function)
mov , %edi
call printMessage
ret
END(sample_assembly_function)
由于 call
是 ret
之前的最后一件事,您也可以只 jmp printMessage
(Tail Call)。当到达 printMessage
中的 ret
时,控制将 return 到调用 sample_assembly_function
的函数。此代码应该有效:
ENTRY(sample_assembly_function)
mov , %edi
jmp printMessage
END(sample_assembly_function)
我正在学习汇编语言,我创建了一个简单的 linux 内核模块来尝试如何从模块调用汇编函数,该函数又调用 C 函数。代码编译得很好。但是,当我尝试插入模块时,它会导致内核崩溃。我从这个post得到了这个想法:Calling C functions from x86 assembly language。我想知道是否有人可以帮助我理解为什么它不起作用。
首先是汇编代码:
#include <linux/linkage.h>
ENTRY(sample_assembly_function)
pushq
call printMessage
add [=11=]x4, %rsp
END(sample_assembly_function)
第二个文件是示例模块文件:
#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);
最后这是 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
当使用 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 作为调用约定时。这与 64 位代码不兼容。
只需执行mov , %rdi
即可加载第一个参数。最好你可以做 mov , %edi
。后者将值 10 移动到 EDI,但由于目标是 32 位寄存器,处理器自动将值零扩展到 64 位寄存器,因此 10 移动到 RDI。后者是较短的编码。
您需要 ret
指令来结束您的汇编语言例程。生成的代码应如下所示:
#include <linux/linkage.h>
ENTRY(sample_assembly_function)
mov , %edi
call printMessage
ret
END(sample_assembly_function)
由于 call
是 ret
之前的最后一件事,您也可以只 jmp printMessage
(Tail Call)。当到达 printMessage
中的 ret
时,控制将 return 到调用 sample_assembly_function
的函数。此代码应该有效:
ENTRY(sample_assembly_function)
mov , %edi
jmp printMessage
END(sample_assembly_function)