应用调试系统调用SWI

Posted zongzi10010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了应用调试系统调用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,原因如下图

    技术分享图片

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

    技术分享图片

  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是一个立即数
    :      );                

输入部

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

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

技术分享图片

损坏部

和输入输出类似,一般用来处理操作的中间过程,因为这些原有的内容都会被损坏,比如上面的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 嵌入式汇编简单介绍

以上是关于应用调试系统调用SWI的主要内容,如果未能解决你的问题,请参考以下文章

如何在Linux上安装swi Prolog的图形化调试器(ubuntu)

Linux软中断与系统调用

Linux系统调用

[国嵌攻略][109][Linux系统调用]

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

按钮在片段中不起作用