前言:本篇我们将讨论如何向系统调用传递的参数,包括单向参数和双向参数的传递,以及如何实现内核态到用户态的批量数据传递。
关于syscall的说明
syscall是x64的系统调用(在用户程序中使用,需要#include<unistd.h>)
其参数通过标签为pt_regs的结构体进行传递,x86_64下的sysdep.h文件给出如下说明
/* The Linux/x86-64 kernel expects the system call parameters in
registers according to the following table:
syscall number rax
arg 1 rdi
arg 2 rsi
arg 3 rdx
arg 4 r10
arg 5 r8
arg 6 r9
......
*/
#define DO_CALL(syscall_name, args) \
lea SYS_ify (syscall_name), %rax; \
syscall
经过测试,syscall调用号通过rax进行传递,其余参数传递顺序为:rdi,rsi,rdx,r10,r8,r9
关于asmlinkage的说明
asmlinkage作用就是告诉编译器,函数参数不是用寄存器来传递,而是用堆栈来传递。
采用asmlinkage,原因是因为用户态在系统调用时进入到内核态,会吧用户态的寄存器全部圧栈,通过合理的构造。正好满足用户态通过寄存器传递参数,内核态通过栈取参数的标准要求。
本实验不采用上面的方式,采用直接用寄存器传参。
1.单向参数传递(传递整型)
static int mycall(struct pt_regs *regs)
{
int res = (regs->di+regs->si+regs->dx+regs->r10);
printk("My syscall is successful!\n");
return res;
}
int main()
{
unsigned long x = 0;
x = syscall(399,101,202,303,404); //测试399号系统调用,增加四个参数
printf("syscall result: %ld\n", x);
return 0;
}
makefile
obj-m:=mycall.o
PWD:= $(shell pwd)
KERNELDIR:= /lib/modules/$(shell uname -r)/build
EXTRA_CFLAGS= -O0
all:
make -C $(KERNELDIR) M=$(PWD) modules
ins:
sudo insmod mycall.ko
rm:
sudo rmmod mycall
.PHONY:test
test:test.c
gcc -g test.c -o test.exe
./test.exe
.PHONY:clean
clean:
make -C $(KERNELDIR) M=$(PWD) clean
rm *.exe
计算结果与预期一致,表明四个整型参数都传递成功
2.双向参数传递(传递指针)
2.1在内核态直接写入用户态内存
/* 采用寄存器传递参数 */
//di,rsi,rdx,r10,r8,r9
static int mycall(struct pt_regs *regs)
{
int* A = (int*)regs->di;
int i;
for(i=0;i<512;i++)
{
A[i]=i;
}
printk("My syscall is successful!\n");
return 0;
}
int A[512];
int main()
{
unsigned long x = 0;
x = syscall(399,A); //测试399号系统调用
int i;
for(i=0;i<512;i++)
{
printf("%d\n", A[i]);
}
return 0;
}