|
一、从用户态访问系统调用
通常,系统调用靠C库支持。用户程序通过包含标准头文件并和C库链接,就可以使用系统调用。但如果你仅仅写出系统调用,glibc库恐怕并不提供支持。 这里有一个好消息还有一个坏消息,好消息是Linux本身提供了一组宏定义linux/include/asm-x86_64/unistd.h文件中。坏消息是在2.6.20之后的内核版本取消了这一系列的宏,导致一开始编译源文件的时候出错,最后在2.6.18中找到了这段代码。其实这段汇编主要的作用就是将系统调用号传递给EAX寄存器,同时将从EAX寄存器取出返回值。
//test.c
#include <stdio.h>
#include <syscall.h>
#include <linux/errno.h>
#include <errno.h>
#define __NR_foo 312
#define MAX_ERRNO 127
#define __syscall "syscall"
#define __syscall_clobber "r11","rcx","memory"
#define __syscall_return(type,res)\
do{\
if((unsigned long)(res) >= (unsigned long)(-MAX_ERRNO)){\
errno = -res;\
res = -1;\
}\
return (type)(res);\
}while(0)
#define _syscall0(type,name)\
type name(void){\
long __res;\
__asm__ volatile(__syscall\
: "=a" (__res)\
: "0" (__NR_##name) : __syscall_clobber );\
__syscall_return(type,__res);\
}
_syscall0(long,foo)
int main(int argc,char** argv)
{
long stack_size = 0;
stack_size = foo();
if(stack_size == -1)
perror("ERROR");
printf("The kernel stack size is %ld\n",stack_size);
return 0;
}
Output: The kernel stack size is 8192
这里面有几点需要注意。
(1)由于我所编译的环境的内核版本是3.2.6,而之前也已经介绍过在2.6.20之后unistd.h文件中不再存在有这些宏,因此宏需要自己声明。上面的宏声明是我从2.6.18内核中拷贝过来的。
(2)除此之外还会遇到一个宏__syscall_return(type,res)用于返回系统调用执行后的返回值。
(3)在_syscallX这一系列宏当中存在一个__syscall宏,在源文件的开始定义为“syscall”,曾经试图去找它的定义,但是没有发现。需要说明的是,在我最初用“int $0x80”而不是“syscall”的时候,系统调用不成功。ERROR返回错误:Dad Address。
(4)在声明_syscall0(long,foo)后面没有分号。
下面的例子是从网上发现的。可以看到通过函数syscall将系统调用号传递进去。这样调用系统调用更方便。
//test1.c
#include <stdio.h>
#include <asm/unistd_64.h>
#include <syscall.h>
#include <sys/syscall.h>
#define __NR_foo 312
int main(int argc,char** argv)
{
long ret = 0;
ret = syscall(__NR_foo);
printf("Thread Stack Size is %ld\n",ret);
return 0;
}
Output: Thread Stack Size is 8192
二、从用户态访问超级调用
以下是在网上发现的一个用户态调用Xen中hypervisor的例子。该例子中类似上面系统调用在Xen中新添加了一个超级调用。
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <xenctrl.h>
#include <xen/sys/privcmd.h>
#define __HYPERVISOR_print_string 39
int main(int argc,char** argv)
{
int fd = 0;
int ret = 0;
char* message = NULL;
if(argc != 2)
{
printf("Please input one parameter!\n");
return -1;
}
message = (char*)malloc(sizeof(char) * strlen(argv[1] + 1));
if (message == NULL) {
printf("malloc failed");
return -1;
}
strcpy(message,argv[1]);
privcmd_hypercall_t hcall = {__HYPERVISOR_print_string, {message,0,0,0,0}};
fd = open("/proc/xen/privcmd",O_RDWR);
if(fd < 0)
{
perror("open");
exit(1);
}
else
printf("fd=%d\n",fd);
ret = ioctl(fd,IOCTL_PRIVCMD_HYPERCALL,&hcall);
perror("ioctl err");
printf("ret = %d\n",ret);
close(fd);
return 0;
}
之后便以Xen,重新加载Xen hypervisor,在xm dmesg命令下即可查看hypercall运行结果。 |
|
|