开始
用两种方式实现使用同一个系统调用:
- 库函数API
- C代码中内嵌汇编
本实验目的是为了熟悉Linux系统调用的相关知识,以及熟悉Linux和嵌入式汇编的写法。
实验内容
选择一个系统调用,列表见这里,我们选择write系统调用。
用C写一个文件test.c,代码如下:1
2
3
4
5
6
7
int main(void)
{
write(1,"hello world!\n",13);
return 0;
}
这里,write函数有三个参数,第一个表示写到终端屏幕上,1可以认为是屏幕的代号,第二个参数是写的内容,第三个参数是写入字符串的长度。
运行结果如下图所示:
然后需要把这段代码转化为嵌入式汇编的格式,嵌入式汇编的格式如下:
在test_asm.c中,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main(void)
{
int a;
char *ch="hello world!\n";
__asm__(
"movl $0x4,%%eax\n\t"//系统调用号存入eax寄存器
"movl $0x1,%%ebx\n\t"//ebx传递参数,第一个参数1
"movl %1,%%ecx\n\t"//ecx存储输出位置,第二个参数字符串ch
"movl $0xd,%%edx\n\t"//edx存储参数长度,第三个参数13
"int $0x80\n\t"//执行系统调用
"movl %%eax,%0\n\t"//系统调用返回值存入eax
:"=m"(a)//输出
:"c"(ch)//输入
);
return 0;
}
执行结果如下所示:
这里代码:1
"movl %1,%%ecx\n\t"//ecx存储输出位置
中的%1,表示传递的第一个参数,它和后面的输入:1
:"c"(ch)//输入
是相呼应的,c表示ecx,表示把字符串a存入ecx中。
而其他的代码:1
"movl $0x1,%%ebx\n\t"//ebx传递参数
$0x1表示把立即数1存入ebx中,也是作为参数的。按照顺序传递参数。
总结
系统调用的三个层次如下所示:
分别为:xyz函数(API)、system call(中断向量)和 sys xyz(中断服务程序)。
当用户进程需要发生系统调用时,CPU 通过软中断切换到内核态开始执行内核系统调用函数。Linux 下有三种发生系统调用的方法:
- 通过 glibc 提供的库函数
- 使用 syscall 函数直接调用
- 通过 int 0x80指令陷入
总的来说,前两种最终都会通过int 0x80指令陷入进入中断处理程序。而系统调用也需要输入输出参数,例如实际的值,用户态进程地址空间的变量的地址,甚至是包含指向用户态函数的指针的数据结构的地址等。
system_call是linux中所有系统调用的入口点,每个系统调用至少有一个参数,即由eax传递的系统调用号,其他参数依次由ebx,ecx,edx,esi,edi,ebp传入。
寄存器传递参数具有如下限制:
- 每个参数的长度不能超过寄存器的长度,即32位
- 在系统调用号(eax)之外,参数的个数不能超过6个(ebx,ecx,edx,esi,edi,ebp)
【版权声明】
本文首发于戚名钰的博客,欢迎转载,但是必须保留本文的署名戚名钰(包含链接)。如您有任何商业合作或者授权方面的协商,请给我留言:qimingyu.security@foxmail.com
欢迎关注我的微信公众号:科技锐新