我正在学习汇编语言,并且创建了一个简单的 Linux 内核模块来尝试如何从模块调用汇编函数,该模块又调用 C 函数。该代码编译良好。但是,当我尝试插入模块时,它会导致内核崩溃。我从这篇文章中得到了这个想法:Calling C functions from x86 assembly language 。我想知道是否有人可以帮助我理解为什么它不起作用。
第一个是汇编代码:
#include <linux/linkage.h>
ENTRY(sample_assembly_function)
pushq $10
call printMessage
add $0x4, %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 时调用约定传递寄存器 RDI、RSI、RDX、RCX、R8 和 R9 中的前 6 个整数类或指针参数。任何剩余的参数都被压入堆栈。您在问题中链接到的代码是 32 位代码,并且使用 Linux i386 System V ABI作为调用约定。这与 64 位代码不兼容。
只需执行 mov $10, %rdi
即可加载第一个参数。最好您可以执行 mov $10, %edi
。后者将值 10 移入 EDI,但由于目标是 32 位寄存器,处理器会自动将值在 64 位寄存器上进行零扩展,因此 10 被移至 RDI。后者是较短的编码。
您需要一个 ret
指令来结束您的汇编语言例程。生成的代码应如下所示:
#include <linux/linkage.h>
ENTRY(sample_assembly_function)
mov $10, %edi
call printMessage
ret
END(sample_assembly_function)
由于 call
是 ret
之前的最后一件事,因此您也可以只 jmp printMessage
( Tail Call )。当到达 printMessage
中的 ret
时,控制权将返回到调用 sample_assemble_function
的函数。此代码应该有效:
ENTRY(sample_assembly_function)
mov $10, %edi
jmp printMessage
END(sample_assembly_function)
关于c - 在 Linux 内核中混合汇编和 C 函数 - x64 模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48633971/