tags: by苏正生 , 原创作品转载请注明出处 ,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
我们一般使用系统调用一般通过库函数的方式
显著地区分:cs寄存器的最低两位表明了当前代码的特权级
在linux中,地址空间是一个显著地标志:
0xc0000000以上的地址空间只能在内核态下访问,
0xc00000000——0xbfffffff的地址空间两种状态下都可以访问。
系统调用(意义):用用户态进程和硬件设备进行交互提供了一组接口。
——把用户从底层的硬件编程中解放出来
API和系统调用的区别:
系统调用的三层皮:
CPU切换到内核态:
方式:通过执行int $0x80来执行系统调用
(中断向量0x80与system_call绑定起来)
由于内核实现了很多不同的系统调用,进程必须指明那个系统调用,所以会需要一个系统调用号的参数。(使用eax寄存器)
系统调用号将xyz和sys_xyz联系起来
系统调用的参数传递方法:
$ man 2 write
参数2代表我们要去看Linux Programmer's Manual
其中有些参数我们需要了解,可以帮助我们进行下面的工作:
// test.c
#include <unistd.h>
int main(){
char string[4] = {'s','z','s','\n'};
write(1,string, 5);
return 0;
}
当我进行执行.c文件的编译的时候,不自觉的和视频教学同步加了-m32,发现我的kali系统里没有相应的头文件,于是进行相关软件包的安装,后来发现不需要-m32,直接编译成64位的代码没有问题。
图中的szs即是我在之前代码中定义的字符串,用一号功能打印在屏幕上。
首先通过查阅资料了解到了一些汇编的知识:
我们用GNU的一组汇编工具就很方便,需要用到的编译连接程序(或者说命令)是as 和 ld。 其实,gcc命令其实就是把c文件编译成一个汇编文件后,再用as 和ld来生成最终程序或者库文件的。
我们都知道用汇编的话就得一个一个寄存器单独来使用了,那怎样进行系统调用呢?方法是严格按照这个顺序将参数传递给寄存器:
* EAX :存放系统调用号,决定进行什么系统调用
* EBX: 存放系统调用第一个参数
* ECX: 存放第二个参数,后面以此类推
* EDX: 第三个
* ESI: 第四个
* EDI:第五个
同时,结合在课程中学习的内容(以time函数为例):
使用库函数API获取系统当前时间
系统调用传递的第一个参数使用ebx,这里是null。
使用eax传递系统调用号,使用的time调用号是13。
函数返回值也使用eax存储,和普通函数一样。
所以对应我们要用的wirte系统调用,要把值传递给相应的寄存器当中:
write(int fd, const void *buf, size_t count);
| | |
----------EBX-------------------ECX-------EDX
这样,我们就可以确定各个寄存器的作用以及传递的都是一些什么参数。
直接用了汇编代码,比C语言复杂不到那里去:
# teat1.s 这一行是注释
.section .data
output: #这是个标签,用来告诉编译器以后我用到“output”就是指这个内存地点
.ascii "i am suzhengsheng,20135333.\n" #这里开辟了一段ascii字符空间,有24个字符
output_end: #另一个标签
.equ len, output_end - output #相当于宏定义,意思是让len 这个变量 等于output_end - output的值
section .text
.globl _start
_start:
movl $4, %eax #依次由前文所述给各个寄存器赋值
movl $1, %ebx
movl $output, %ecx
movl $len, %edx
int $0x80 #进行一次系统调用
end:
movl $1, %eax #这里赋值以后又进行了一次系统调用,1这个系统调用是干什么的呢?
movl $0, %ebx #不知道的话可以去看/usr/include/asm/unistd_32.h求解哦,呵呵
int $0x80
本次实验我通过调用4号调用write来进行了模拟系统调用的过程。对于以往繁杂的系统调用忽然有了亲切和清晰地理解。在上面已经把每一部分在做什么,怎么做都写了,就不再这里繁复的阐述了。
最后比较了一下两个代码,虽然完成的是同样的系统调用,但是通过比较可以看出汇编代码在体积方面有很大的优势:
实验中遇到了自己的虚拟机安装的kali系统编译32位程序不成功的情况,目测是32位的搜索路径下没有sys/cdefs.h
但是我想说的是,64位的机器已经是一个趋势了,代表了更快的速度和更高的效率,代码等都应该向着64位逐步优化。
网上有人这么说:
“一般64位系统最好不要安装32位库的-dev,经常有冲突,强行安装会破坏64位环境,头文件并不像共享库分得那么完美。
我觉得比较好的做法是,重新把某一个文件夹当成rootfs,在里面安装各种32位的库和库-dev,然后给gcc传递参数"--sysroot=路径"就可以让gcc重新选取那个文件夹为rootfs,而不再认为/为rootfs。其实这样就变成交叉编译了。也可以在这个rootfs中安装32位的必要软件包如bash,gcc,coreutils等等,然后chroot进去就变成host编译了。其实有个比较简单的方法,就是虚拟机下安一个32位的ubuntu,然后装好各种软件包,然后直接对/打包,再拿出来解压就是rootfs了,后面最多就是缺啥安啥,工作量比较小”
虽有偏颇,但还是有参考意义,在这里分享给大家。