1首先来说讲应用程序怎样兑现系统调用(用户态->内核态)?

壹首先来说讲应用程序如何兑现系统调用(用户态->内核态)?

1.在之前第36章里,大家学习了通过驱动的oops定位错误代码行

1.在之前第36章里,大家上学了经过驱动的oops定位错误代码行

我们以应用程序的write()函数为例:

我们以应用程序的write()函数为例:

修改内核来打印用户态的oops,Linux应用调试。第36章的oops代码如下所示:

第36章的oops代码正如所示:

1)先是用户态的write()函数会跻身glibc库,里面会将write()转变为swi(Software Interrupt)指令,从而发出软件中断,swi指令如下所示:

1)率先用户态的write()函数会进来glibc库,里面会将write()转变为swi(Software Interrupt)指令,从而发出软件中断,swi指令如下所示:

Unable to handle kernel paging request at virtual address 56000050
      //无法处理内核页面请求的虚拟地址56000050
pgd = c3850000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
        //内部错误oops
Modules linked in: 26th_segmentfault
        //表示内部错误发生在26th_segmentfault.ko驱动模块里
CPU: 0    Not tainted  (2.6.22.6 #2)
PC is at first_drv_open+0x78/0x12c [26th_segmentfault]
        //PC值:程序运行成功的最后一次地址,位于first_drv_open()函数里,偏移值0x78,该函数总大小0x12c
LR is at 0xc0365ed8             //LR值

/*发生错误时的各个寄存器值*/
pc : [<bf000078>]    lr : [<c0365ed8>]    psr: 80000013
sp : c3fcbe80  ip : c0365ed8  fp : c3fcbe94
r10: 00000000  r9 : c3fca000  r8 : c04df960
r7 : 00000000  r6 : 00000000  r5 : bf000de4  r4 : 00000000
r3 : 00000000  r2 : 56000050  r1 : 00000001  r0 : 00000052

Flags: Nzcv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33850000  DAC: 00000015
Process 26th_segmentfau (pid: 813, stack limit = 0xc3fca258)
            //发生错误时,进程名称为26th_segmentfault

Stack: (0xc3fcbe80 to 0xc3fcc000)        //栈信息,从栈底0xc3fcbe80到栈顶0xc3fcc000
be80: c06d7660 c3e880c0 c3fcbebc c3fcbe98 c008d888 bf000010 00000000 c04df960
bea0: c3e880c0 c008d73c c0474e20 c3fb9534 c3fcbee4 c3fcbec0 c0089e48 c008d74c
bec0: c04df960 c3fcbf04 00000003 ffffff9c c002c044 c380a000 c3fcbefc c3fcbee8
bee0: c0089f64 c0089d58 00000000 00000002 c3fcbf68 c3fcbf00 c0089fb8 c0089f40
bf00: c3fcbf04 c3fb9534 c0474e20 00000000 00000000 c3851000 00000101 00000001
bf20: 00000000 c3fca000 c04c90a8 c04c90a0 ffffffe8 c380a000 c3fcbf68 c3fcbf48
bf40: c008a16c c009fc70 00000003 00000000 c04df960 00000002 be84ce38 c3fcbf94
bf60: c3fcbf6c c008a2f4 c0089f88 00008588 be84ce84 00008718 0000877c 00000005
bf80: c002c044 4013365c c3fcbfa4 c3fcbf98 c008a3a8 c008a2b0 00000000 c3fcbfa8
bfa0: c002bea0 c008a394 be84ce84 00008718 be84ce30 00000002 be84ce38 be84ce30
bfc0: be84ce84 00008718 0000877c 00000003 00008588 00000000 4013365c be84ce58
bfe0: 00000000 be84ce28 0000266c 400c98e0 60000010 be84ce30 30002031 30002431

Backtrace:                                        //回溯信息
[<bf000000>] (first_drv_open+0x0/0x12c [26th_segmentfault]) from [<c008d888>] (chrdev_open+0x14c/0x164)
 r5:c3e880c0 r4:c06d7660
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
 r8:c3fb9534 r7:c0474e20 r6:c008d73c r5:c3e880c0 r4:c04df960
[<c0089d48>] (__dentry_open+0x0/0x1e8) from [<c0089f64>] (nameidata_to_filp+0x34/0x48)
[<c0089f30>] (nameidata_to_filp+0x0/0x48) from [<c0089fb8>] (do_filp_open+0x40/0x48)
 r4:00000002
[<c0089f78>] (do_filp_open+0x0/0x48) from [<c008a2f4>] (do_sys_open+0x54/0xe4)
 r5:be84ce38 r4:00000002
[<c008a2a0>] (do_sys_open+0x0/0xe4) from [<c008a3a8>] (sys_open+0x24/0x28)
[<c008a384>] (sys_open+0x0/0x28) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
Code: bf000094 bf0000b4 bf0000d4 e5952000 (e5923000)

Segmentation fault 
Unable to handle kernel paging request at virtual address 56000050
      //无法处理内核页面请求的虚拟地址56000050
pgd = c3850000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
        //内部错误oops
Modules linked in: 26th_segmentfault
        //表示内部错误发生在26th_segmentfault.ko驱动模块里
CPU: 0    Not tainted  (2.6.22.6 #2)
PC is at first_drv_open+0x78/0x12c [26th_segmentfault]
        //PC值:程序运行成功的最后一次地址,位于first_drv_open()函数里,偏移值0x78,该函数总大小0x12c
LR is at 0xc0365ed8             //LR值

/*发生错误时的各个寄存器值*/
pc : [<bf000078>]    lr : [<c0365ed8>]    psr: 80000013
sp : c3fcbe80  ip : c0365ed8  fp : c3fcbe94
r10: 00000000  r9 : c3fca000  r8 : c04df960
r7 : 00000000  r6 : 00000000  r5 : bf000de4  r4 : 00000000
r3 : 00000000  r2 : 56000050  r1 : 00000001  r0 : 00000052

Flags: Nzcv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33850000  DAC: 00000015
Process 26th_segmentfau (pid: 813, stack limit = 0xc3fca258)
            //发生错误时,进程名称为26th_segmentfault

Stack: (0xc3fcbe80 to 0xc3fcc000)        //栈信息,从栈底0xc3fcbe80到栈顶0xc3fcc000
be80: c06d7660 c3e880c0 c3fcbebc c3fcbe98 c008d888 bf000010 00000000 c04df960
bea0: c3e880c0 c008d73c c0474e20 c3fb9534 c3fcbee4 c3fcbec0 c0089e48 c008d74c
bec0: c04df960 c3fcbf04 00000003 ffffff9c c002c044 c380a000 c3fcbefc c3fcbee8
bee0: c0089f64 c0089d58 00000000 00000002 c3fcbf68 c3fcbf00 c0089fb8 c0089f40
bf00: c3fcbf04 c3fb9534 c0474e20 00000000 00000000 c3851000 00000101 00000001
bf20: 00000000 c3fca000 c04c90a8 c04c90a0 ffffffe8 c380a000 c3fcbf68 c3fcbf48
bf40: c008a16c c009fc70 00000003 00000000 c04df960 00000002 be84ce38 c3fcbf94
bf60: c3fcbf6c c008a2f4 c0089f88 00008588 be84ce84 00008718 0000877c 00000005
bf80: c002c044 4013365c c3fcbfa4 c3fcbf98 c008a3a8 c008a2b0 00000000 c3fcbfa8
bfa0: c002bea0 c008a394 be84ce84 00008718 be84ce30 00000002 be84ce38 be84ce30
bfc0: be84ce84 00008718 0000877c 00000003 00008588 00000000 4013365c be84ce58
bfe0: 00000000 be84ce28 0000266c 400c98e0 60000010 be84ce30 30002031 30002431

Backtrace:                                        //回溯信息
[<bf000000>] (first_drv_open+0x0/0x12c [26th_segmentfault]) from [<c008d888>] (chrdev_open+0x14c/0x164)
 r5:c3e880c0 r4:c06d7660
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
 r8:c3fb9534 r7:c0474e20 r6:c008d73c r5:c3e880c0 r4:c04df960
[<c0089d48>] (__dentry_open+0x0/0x1e8) from [<c0089f64>] (nameidata_to_filp+0x34/0x48)
[<c0089f30>] (nameidata_to_filp+0x0/0x48) from [<c0089fb8>] (do_filp_open+0x40/0x48)
 r4:00000002
[<c0089f78>] (do_filp_open+0x0/0x48) from [<c008a2f4>] (do_sys_open+0x54/0xe4)
 r5:be84ce38 r4:00000002
[<c008a2a0>] (do_sys_open+0x0/0xe4) from [<c008a3a8>] (sys_open+0x24/0x28)
[<c008a384>] (sys_open+0x0/0x28) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
Code: bf000094 bf0000b4 bf0000d4 e5952000 (e5923000)

Segmentation fault 
swi   #val   //val: bit[23:0]立即数,该val用来判断用户函数需要调用哪个内核函数
swi   #val   //val: bit[23:0]立即数,该val用来判断用户函数需要调用哪个内核函数

一.一那干什么在**上一章**,大家用错误的应用程序,却不曾打字与印刷oops,如下图所示:

1.一那为啥在**上一章**,大家用错误的应用程序,却尚无打字与印刷oops,如下图所示:

2)接下来CPU会跳到不行向量入口vector_swi处,依照swi指令前面包车型客车val值,在有个别数组表里找到相应的sys_write()函数

2)接下来CPU会跳到那多少个向量入口vector_swi处,依据swi指令前边的val值,在某个数组表里找到相应的sys_write()函数

图片 1

图片 2

代码如下所示(位于arch\arm\kernel\entry-common.S):

代码如下所示(位于arch\arm\kernel\entry-common.S):

接下去,大家便来安顿基本,从而打字与印刷应用程序的oops

接下去,大家便来配置基本,从而打字与印刷应用程序的oops

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
  ... ...

       ldr   scno, [lr, #-4]                 @ get SWI instruction  //获取SWI值
    A710(       and  ip, scno, #0x0f000000 @ check for SWI)
    A710(       teq  ip, #0x0f000000)                               //校验SWI的bit[27:24]是否为0xf
    A710(       bne  .Larm710bug)
     ... ...

       enable_irq                           //调用enable_irq()函数
       get_thread_info tsk
       adr  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个字节
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
  ... ...

       ldr   scno, [lr, #-4]                 @ get SWI instruction  //获取SWI值
    A710(       and  ip, scno, #0x0f000000 @ check for SWI)
    A710(       teq  ip, #0x0f000000)                               //校验SWI的bit[27:24]是否为0xf
    A710(       bne  .Larm710bug)
     ... ...

       enable_irq                           //调用enable_irq()函数
       get_thread_info tsk
       adr  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基值为0x捌仟00,相当于说要调用数组表的率先个函数时,则利用:

从地方代码能够看看,2440的val基值为0x800000,也等于说要调用数组表的率先个函数时,则使用:

二.首先来找出oops里的:Unable to handle
kernel打字与印刷语句,看在哪些函数打字与印刷的

2.先是来搜索oops里的:Unable to handle
kernel打印语句,看在哪个函数打字与印刷的

swi  #0x900000
swi  #0x900000

一般来讲图所示,找到位于__do_kernel_fault()函数中:

如下图所示,找到位于__do_kernel_fault()函数中:

 

 

 图片 3

 图片 4

2接下去,大家便出自制二个种类调用

2接下去,大家便来源于制3个系统调用

 

 

  • 1)在基础中,仿照3个sys_hello函数,然后放入数组表,供swi调用
  • 2)写应用程序,直接通过swi指令,来调用sys_hello函数
  • 1)在根本中,仿照二个sys_hello函数,然后放入数组表,供swi调用
  • 2)写应用程序,直接通过swi指令,来调用sys_hello函数

3.继续找,发现__do_kernel_fault()被do_bad_area()调用

3.继续找,发现__do_kernel_fault()被do_bad_area()调用

 

 

 图片 5

 图片 6

3
仿照sys_hello()

3
仿照sys_hello()

do_bad_area()函数,从字面上剖判,表示代码实行到不当段地点

do_bad_area()函数,从字面上分析,表示代码施行到不当段位置

三.壹先来探求数组表,以sys_write为例,寻觅找到位于arch/arm/kernel/calls.S,如下图所示:

3.一先来探索数组表,以sys_write为例,找搜索到位于arch/arm/kernel/calls.S,如下图所示:

其中user_mode(regs)函数,通过决断CPSCR-V寄存器假设用户方式则再次来到0,不然重临正数.

其中user_mode(regs)函数,通过剖断CPS奥德赛寄存器如果用户情势则再次来到0,不然再次来到正数.

图片 7

图片 8

故而大家上1章的荒唐的应用程序便会调用__do_user_fault()函数

于是大家上壹章的一无所能的应用程序便会调用__do_user_fault()函数

中间CALL定义如下所示:

里头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字节
.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字节

4.__do_user_fault()函数如下所示:

4.__do_user_fault()函数如下所示:

 

 

 图片 9

 图片 10

三.二所以我们在call.S文件的CALL()列表的末段增添壹段,
如下图所示, sys_hello()的val值为352:

三.二所以大家在call.S文件的CALL()列表的尾声加多1段,
如下图所示, sys_hello()的val值为352:

从上海体育场面来看,要想打字与印刷应用程序的错误音信,还亟需:

从上图来看,要想打字与印刷应用程序的错误音讯,还亟需:

 图片 11

 图片 12

三.1配备基本,设置宏CONFIG_DEBUG_USER(只要宏是以“CONFIG_”伊始,都是与安顿相关)

3.一配备基本,设置宏CONFIG_DEBUG_USER(只要宏是以“CONFIG_”起初,都以与配置相关)

 

 

1)在make menuconfig里搜索DEBUG_USEOdyssey,如下图所示:

1)在make menuconfig里搜索DEBUG_USEPRADO,如下图所示:

3.3
fs\read_write.c文件里写一个sys_hello()函数

3.3
fs\read_write.c文件里写三个sys_hello()函数

 图片 13

 图片 14

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);
    }
}
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);
    }
}

为此将Kernel hacking-> Verbose user fault
messages 置为Y,并再一次烧内核

因而将Kernel hacking-> Verbose user fault
messages 置为Y,一视同仁复烧内核

3.4 
include\linux\syscalls.h文件里声称sys_hello()

3.4 
include\linux\syscalls.h文件里声称sys_hello()

3.2使if
(user_debug & UDBG_SEGV)为真

3.2使if
(user_debug & UDBG_SEGV)为真

asmlinkage void sys_hello(const char __user * buf, size_t count);
asmlinkage void sys_hello(const char __user * buf, size_t count);

1)其中user_debug定义如下所示:

1)其中user_debug定义如下所示:

 

 

 图片 15

 图片 16

四.写应用程序

4.写应用程序

明明当uboot传递进入的授命行字符里含有”user_debug=”时,便会调用user_debug_setup()->get_option(),最后会将”user_debug=”前面带的字符串提取给user_debug变量.

无人不知当uboot传递进入的一声令下行字符里含有”user_debug=”时,便会调用user_debug_setup()->get_option(),最后会将”user_debug=”后边带的字符串提取给user_debug变量.

网站地图xml地图