zoukankan      html  css  js  c++  java
  • 应用调试(四)系统调用SWI


    title: 应用调试(四)系统调用SWI
    date: 2019/01/19 18:05:39
    toc: true

    应用调试(四)系统调用SWI

    系统调用

    我们App中的open,read等实际上会触发swi异常,触发系统调用sys_open,sys_read等,内核根据swi的值来执行具体的操作

    SWI代码片段分析

    搜索下vector_swi,找到入口函数archarmkernelentry-common.S

    	.align	5
    ENTRY(vector_swi)
    	@ 保存现场
    	
    	sub	sp, sp, #S_FRAME_SIZE
    	stmia	sp, {r0 - r12}			@ Calling r0 - r12
    	add	r8, sp, #S_PC
    	stmdb	r8, {sp, lr}^			@ Calling sp, lr
    	mrs	r8, spsr			@ called from non-FIQ mode, so ok.
    	str	lr, [sp, #S_PC]			@ Save calling PC
    	str	r8, [sp, #S_PSR]		@ Save CPSR
    	str	r0, [sp, #S_OLD_R0]		@ Save OLD_R0
    	zero_fp
    
    	@ 获得swi的指令地址,确保是swi指令
    	ldr	scno, [lr, #-4]			@ get SWI instruction
        A710(	and	ip, scno, #0x0f000000		@ check for SWI		)
        A710(	teq	ip, #0x0f000000						)
        A710(	bne	.Larm710bug						)
        
        @ tbl等于数组表基地址
        get_thread_info tsk
        adr	tbl, sys_call_table		@ load syscall table pointer
        ldr	ip, [tsk, #TI_FLAGS]		@ check for syscall tracing
    
        @清除高8位
        bic	scno, scno, #0xff000000		@ mask off SWI op-code
        @ #define __NR_SYSCALL_BASE	0x900000  这里swi的值实际上是0x900000 0x900001 ...所以要清除这个高位的9
        eor	scno, scno, #__NR_SYSCALL_BASE	@ check OS number
    
        @根据索引号,去tbl 这个数组中调用函数
        @ tbl:数组表基地址,  scno:要调用的sys_write()的索引值     lsl #2:左移2位,一个函数指针占据4个字节
        cmp	scno, #NR_syscalls		@ check upper syscall limit
        adr	lr, ret_fast_syscall		@ return address
        ldrcc	pc, [tbl, scno, lsl #2]		@ call sys_* routine
    
    1. 这里首先获得swi这条指令的内容,swi指令位于lr-4,原因如下图

      mark

    2. 然后分析确保是swi指令,也就是and ip, scno, #0x0f000000

      mark

    3. 获得全局的一个存有系统调用函数的数组的地址

    4. 通过swi的值去找到这个数组的索引,执行函数

    分析sys_write

    理论上,应该有sys_write存入这个指针数组,搜索下发现如下archarmkernelcalls.S

    /* 0 */		CALL(sys_restart_syscall)
    		CALL(sys_exit)
    		CALL(sys_fork_wrapper)
    		CALL(sys_read)
    		CALL(sys_write)
    /* 5 */		CALL(sys_open)
    ......
    

    同时有如下在archarmkernelentry-common.S,也就是先定义这个CALL,再将上面的定义全部包含进来

    	.equ NR_syscalls,0
    #define CALL(x) .equ NR_syscalls,NR_syscalls+1
    #include "calls.S"
    #undef CALL
    #define CALL(x) .long x
    
    

    也就是说,我们可以自己定义一个swi val 在archarmkernelcalls.S 放在最后面

    /* 350 */	CALL(sys_timerfd)
    		CALL(sys_eventfd)
    		CALL(sys_hello)		/* 添加一个自己的系统调用 */
    

    构造sys_hello

    仿照sys_write声明定义在includelinuxsyscalls.hfs ead_write.c

    asmlinkage ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count)
    

    实现函数如下

    asmlinkage void sys_hello(const char __user * buf, size_t count);
    
    asmlinkage void sys_hello(const char __user * buf, size_t count)
    {
        char ker_buf[100];
    
        if(buf)
        { copy_from_user(ker_buf, buf, (count<100)? count : 100);
          ker_buf[99]='';
          printk("sys_hello:%s
    ",ker_buf);
        }
    }
    

    应用程序调用SWI

    参考glibc-2.3.6/,这里没去仔细看了,这里有个__brk函数,仿照着写,具体看下注释

    #include <errno.h>
    #include <unistd.h>
    #define __NR_SYSCALL_BASE	0x900000
    
    void hello(char *buf, int count)
    {
    	/* swi */
    asm ("mov r0, %0
    "   /* save the argment in r0 */
    	 "mov r1, %1
    "   /* save the argment in r0 */
    	 "swi %2
    "   /* do the system call */
    	 
    	 /* 输出部分,这里不需要输出,但是需要 : 占位*/
    	 :	
    	 /* 输入部分, r表示寄存器 ,使用 %0 表示第一个*/
    	 /* %0        %1          i表示立即数,也是就是Immediate  这里就是 swi的具体的值 */
    	 : "r"(buf), "r"(count), "i" (__NR_SYSCALL_BASE + 352)
    	 /* 损坏部,指令执行过程中可能引起的哪些寄存器发生变化*/
    	 : "r0", "r1");
    }
    
    int main(int argc, char **argv)
    {
    	printf("in app, call hello
    ");
    	hello("hello", 6);
    	return 0;
    }
    

    嵌入汇编语法

    参考文件cnblog,这里不去仔细分析了,简单的分析在韦老师视频31课4.1节25分左右

    参考linux内核源代码情景分析1.5.2节)

    格式如下所示:

    • asm( 指令部 : 输出部 : 输入部 : 损坏部 );
    • 如果没有的部分,冒号也不能省略

    指令部

    在指令部中,若出现%0、%1、%2等,则表示指令部后面的第几个变量.

    比如上面代码的mov r0, %0

    其中%0便会对应buf值,而r是一个约束条件字母,r表示任意一个寄存器,在预处理时,便会自动分配一个寄存器,将buf值放入该寄存器里,然后运行mov r0 (buf对应的寄存器)

    输出部

    每个输出部的约束条件字母都要加上"=",比如:

    int num=5,val;
    
    asm("mov %0,%1
    "
        :"=r"(val)                //指定val是一个输出部,执行mov后,val便等于5
        :"i"(num)                // "i"约束条件字母,表示num是一个立即数
        :      );                
    

    输入部

    和输出部唯一不同的就是,在约束条件字母前不能加上=

    常用的约束条件字母,如下图所示:

    mark

    损坏部

    和输入输出类似,一般用来处理操作的中间过程,因为这些原有的内容都会被损坏,比如上面的hello()里的r0, r1,只是用来当做参数,传递给内核的sys_hello()

    测试APP

    # mount -t nfs -o nolock,vers=2 192.168.95.222:/home/book/stu /mnt
    # cd /mnt/code
    # ./test_system_call
    in app, call hello
    sys_hello:hello   #  这个“hello” 是系统调用打印的
    

    参考

    https://www.cnblogs.com/lifexy/p/8075282.html 嵌入式汇编简单介绍

  • 相关阅读:
    maven的groupId和artifactId的区别
    二级域名和一级域名的区别
    单点登陆系统
    公有链,私有链,联盟链
    比特币与区块链是父与子关系吗
    比特币钱包:核心钱包与轻钱包
    区块链到底是什么
    打造信任机器——区块链技术及其应用
    区块广播
    区块链技术理念
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10292717.html
Copyright © 2011-2022 走看看