42.Linux应用调试-初步制作系统调用(用户态-内核态)
1首先来讲讲应用程序如何实现系统调用(用户态->内核态)?
我们以应用程序的write()函数为例:
1)首先用户态的write()函数会进入glibc库,里面会将write()转换为swi(Software Interrupt)指令,从而产生软件中断,swi指令如下所示:
swi #val //val: bit[23:0]立即数,该val用来判断用户函数需要调用哪个内核函数
2)然后CPU会跳到异常向量入口vector_swi处,根据swi指令后面的val值,在某个数组表里找到对应的sys_write()函数
代码如下所示(位于arch\arm\kernel\entry-common.S):
ENTRY(vector_swi) /*保护用户态的现场*/ sub sp, sp, #S_FRAME_SIZEstmia sp, {r0 - r12} @ Calling r0 - r12add r8, sp, #S_PCstmdb r8, {sp, lr}^ @ Calling sp, lrmrs r8, spsr @ called from non-FIQ mode, so ok.str lr, [sp, #S_PC] @ Save calling PCstr r8, [sp, #S_PSR] @ Save CPSRstr r0, [sp, #S_OLD_R0] @ Save OLD_R0zero_fp... ...ldr scno, [lr, #-4] @ get SWI instruction //获取SWI值A710( and ip, scno, #0x0f000000 @ check for SWI)A710( teq ip, #0x0f000000) //校验SWI的bit[27:24]是否为0xfA710( bne .Larm710bug)... ...enable_irq //调用enable_irq()函数get_thread_info tskadr tbl, sys_call_table @ load syscall table pointer // tbl等于数组表基地址ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing ... ...bic scno, scno, #0xff000000 @ mask off SWI op-code //只保留SWI的bit[23:0],也就是val值 eor scno, scno, #__NR_SYSCALL_BASE @ check OS number //对于2440而讲,__NR_SYSCALL_BASE基地址等于0x900000,也就是说val值为0x900000时,异或后,scno则等于0,表示数组表的基地址(第一个函数位置) ... ...ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine //pc=(tbl+scno)<<2,实现调用sys_write()//tbl:数组表基地址, scno:要调用的sys_write()的索引值 lsl #2:左移2位,一个函数指针占据4个字节
从上面代码可以看出,2440的val基值为0x900000,也就是说要调用数组表的第一个函数时,则使用:
swi #0x900000
2 接下来,我们便来自制一个系统调用
- 1)在内核中,仿照一个sys_hello函数,然后放入数组表,供swi调用
- 2)写应用程序,直接通过swi指令,来调用sys_hello函数
3 仿照sys_hello()
3.1先来查找数组表,以sys_write为例,搜索找到位于arch/arm/kernel/calls.S,如下图所示:
其中CALL定义如下所示:
.equ NR_syscalls,0 //将NR_syscalls=0#define CALL(x) .equ NR_syscalls,NR_syscalls+1 //将CALL(x) 定义为:NR_syscalls=NR_syscalls+1 ,也就是每有一个CALL(),则该CALL值则+1#include "calls.S" //将calls.S的内容包进来,CALL(x)上面已经有了定义,就会将calls.S里面的所有CALL(sys_xx)排列起来#undef CALL //撤销CALL定义#define CALL(x) .long x //然后再将排列起来的sys_xx以long(4字节)对齐,一个函数指针占据4字节
3.2 所以我们在call.S文件的CALL()列表的最后添加一段, 如下图所示, sys_hello()的val值为352:
3.3 fs\read_write.c文件里写一个sys_hello()函数
asmlinkage void sys_hello(const char __user * buf, size_t count) //打印count长数据 {char ker_buf[100];if(buf){ copy_from_user(ker_buf, buf, (count<100)? count : 100);ker_buf[99]='\0';printk("sys_hello:%s\n",ker_buf);} }
3.4 include\linux\syscalls.h文件里声明sys_hello()
asmlinkage void sys_hello(const char __user * buf, size_t count);
4.写应用程序
#include <errno.h> #include <unistd.h> #define __NR_SYSCALL_BASE 0x900000void hello(char *buf, int count) {/* swi */asm ("mov r0, %0\n" /* save the argment in r0 */ //%0等于buf "mov r1, %1\n" /* save the argment in r0 */ //%1等于count"swi %2\n" /* do the system call */ //%2等于0x900352: //输出部: "r"(buf), "r"(count), "i" (__NR_SYSCALL_BASE + 352) //输入部: "r0", "r1"); //损坏部,指原有的数据会被破坏 } int main(int argc, char **argv) {printf("in app, call hello\n");hello("www.100ask.net", 15);//这个函数会调用内核的sys_hello()return 0; }
4.1 其中asm ()是一个内嵌汇编(参考linux内核源代码情景分析1.5.2节)
格式如下所示:
- asm( 指令部 : 输出部 : 输入部 : 损坏部 );
指令部
在指令部中,若出现%0、%1、%2等,则表示指令部后面的第几个变量.
比如上面代码的"mov r0, %0\n".
其中%0便会对应buf值,而"r"是一个约束条件字母,r表示任意一个寄存器,在预处理时,便会自动分配一个寄存器,将buf值放入该寄存器里,然后运行mov r0 (buf对应的寄存器)
输出部
每个输出部的约束条件字母都要加上"=",比如:
int num=5,val;asm("mov %0,%1\n":"=r"(val) //指定val是一个输出部,执行mov后,val便等于5:"i"(num) // "i"约束条件字母,表示num是一个立即数: );
输入部
和输出部唯一不同的就是,在约束条件字母前不能加上"="
常用的约束条件字母,如下图所示:
损坏部
和输入输出类似,一般用来处理操作的中间过程,因为这些原有的内容都会被损坏,比如上面的hello()里的"r0", "r1",只是用来当做参数,传递给内核的sys_hello()
5.重新烧写内核,试验应用程序
如上图所示,一个简单的系统调用便OK了
调用成功后,就可以来修改sys_hello(),来打印应用程序的各个寄存器值,打断点,来实现调试应用程序,需要用到:
task_pt_regs(current); //获取当前应用程序的各个寄存器内容,会返回一个pt_regs结构体
42.Linux应用调试-初步制作系统调用(用户态-内核态)相关推荐
- 5分钟搞懂用户态,内核态
5分钟搞懂用户态,内核态 1. 什么是用户态,内核态 用户态就是提供应用程序运行的空间,为了使应用程序访问到内核管理的资源例如CPU,内存,I/O.内核必须提供一组通用的访问接口,这些接口就叫系统调用 ...
- 用户态/内核态、用户栈/内核栈
一.用户态和内核态 内核态和用户态是操作系统的两种运行级别,用于区分不同程序的不同权利. 内核态就是拥有资源多的状态,或者说访问资源多的状态,也称为特权态.相对来说,用户态就是非特权态,访问的而资源将 ...
- 操作系统用户态内核态线程同步
1.计算机系统中有两类程序:系统程序和应用程序.为了保证系统程序不被应用程序破坏,为计算机设置了两种状态:内核态和用户态. 内核态运行的程序可以访问计算机的任何资源不受限制. 用户态运行的程序可以直接 ...
- Linux预备知识(三):系统调用-用户态/内核态
1)示例 void testfork() { if(0 = = fork()){ printf("create new process success!\n"); }printf( ...
- 【Linux 内核】Linux 内核体系架构 ( 硬件层面 | 内核空间 | 用户空间 | 内核态与用户态切换 | 系统调用 | 体系结构抽象层 )
文章目录 一.Linux 内核体系架构 二.内核态与用户态切换 ( 系统调用层 ) 三.体系结构抽象层 一.Linux 内核体系架构 Linux 内核最初的源码不足一万行 , 当前的 Linux 内核 ...
- linux 用户态 内核态 通信,procfs(从0开始,内核态和用户态通信charpter2)
这篇博文将针对linux内核态与用户态通信方式中的procfs进行详细的学习. /proc主要存放内核的一些控制信息,所以这些信息大部分的逻辑位置位于内核控制的内存,在/proc下使用ls -l你会发 ...
- netlink实现与使用方法详解(用户态/内核态)
一.什么是netlink Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口. 在Linux 内核中,使用netlink ...
- 理解用户态切换到内核态——内核态下有一个特殊的进程
现在想想,从用户态进入到内核态,相当于一次进程切换--这就好像内核态下有一个特殊的进程. 我就把进入内核态后,理解为进入了一个特殊的进程,一切都忽然合理了,恍然大悟--所以所有用户态的task(称之为 ...
- 【Java 并发编程】线程池机制 ( 测试线程开销 | 启动线程分析 | 用户态 | 内核态 | 用户线程 | 内核线程 | 轻量级进程 )
文章目录 一.测试线程开销 1.正常测试 2.不创建线程 3.只创建不启动线程 4.只启动不等待执行完成 二.分析测试结果 1.启动线程分析 2.用户线程与内核线程 3.轻量级进程 4.验证 Java ...
最新文章
- 测试人员的GitHub
- Spring Cloud Alibaba基础教程:@SentinelResource注解实现限流控制与熔断降级使用详解
- vscode / 杂项
- where is SAP CRM One Order event callback registered
- android+坐标类,Android Path和PathMeasure类的使用之获取圆弧上的坐标值
- 站怎么点都是一样_搞笑段子:都说女人是水做的,你怎么不太一样
- 一道有关switch-case题目
- 解决Required String parameter xxx is not present异常
- 微信小程序 java通过 rawData 和 session_key 生成 signature 签名
- 理解拉普拉斯平滑 Laplace Smoothing
- 一文搞懂函数计算及其工作原理
- 凹点匹配 matlab源码,粘连类圆形目标图像的分割方法与流程
- 【C++】-- STL容器适配器之priority_queue
- Android开关按键(左右复选)
- vue实现购物车简单的功能-单选全选总价计算、批量删除
- 斗地主十句口诀技巧全解
- Python爬虫项目整理
- matlab三维曲线的绘制
- 如何使用射手影音寻找字幕
- 图像处理--gamma矫正/Python
热门文章
- 当ASP.NET Forms验证方式遭遇苹果IOS
- 总结一下HtmlAgilityPack
- NetAdvangate Infragisticss 控件在工程移动到别的机器上,引用失效问题
- PHP+MySQL 注射资料
- 上周ASP.NET英文技术文章推荐[05/06 - 05/12]
- JSP的9个内置对象-application
- Golang——时间日期函数
- 校办研修之计算机培训简报,“2018校本研修培训”第二期学习简报
- 4变形物体_Houdini基础(二)曲线变形物体
- mysql json mybatis_mybatis支持json,Spring boot配置