哲学原理41.Linux以调试-修改外审查来打印用户态的oops

by admin on 2018年12月16日

 

1.在之前第36章里,我们念了经过让的oops定位错误代码行

智力题
   
1.烧同根本不净匀的索,从头烧到尾总共需要1只钟头,问什么用烧绳子的主意来确定半时之光阴也?

第36章的oops代码一般来说所示:

有数止一样由发热吧!!

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 

      
还有确定十五秒钟:可以行使三清。 

1.1那么为什么在**上一章**,大家由此错误的应用程序,却尚无打印oops,如下图所示:

2.10个海盗抢到了100颗宝石,每一样颗都一律大小都价值连城。他们操纵这么分:
 
     (1)抽签决定好的号(1~10);  
     (2)首先,由1号提议分配方案,然后大家决定,当且仅当过半数的人
同意时,遵照他的方案展开分配,否则将吃撇下上大海喂鲨鱼;  
     (3)假若1如泣如诉大后,再由2号指出分配方案,然后剩下的4只人展开裁定,
当且仅当跨越一半底人数同意时,依据他的方案展开分配,否则用被扔入大海喂鲨鱼
     (4)依此类推??  
   
条件:每个海盗仍旧丰硕聪明伶俐之总人口,都能杀理智地做出判断,从而做出取舍。  

哲学原理 1

   
问题:第一个海盗提议什么的分红方案才能够如自己的入账最大化?
 

连片下,大家虽来安排基本,从而打印应用程序的oops

未解

 

    3.怎么下水道的壳是圆之?  

2.率先来搜索oops里之:Unable to handle
kernel打印语句,看在什么人函数打印的

为丁是圆满之哇!

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

 

 哲学原理 2

    4.中华来微辆汽车?
 

 

非常多

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

   
5.君被工人也公办事7天,回报是一样干净金条,那穷金条平分成相连的7段,你必以每天停止的当儿被他们相同段金条。若是只是同意你少次等把金条弄断,你只要
何给您的工人付费?  

 哲学原理 3

分1,2,4。  

do_bad_area()函数,从字面上析,表示代码执行到错误段地方

6.闹平等辆火车因每时15公里之快离开东京(Tokyo)直奔维也纳,同时其余一样部列车为各级时20英里的速度从圣菲波哥大启幕为京城。倘使来同一可是鸟,以30公里各时辰之快慢及有限部列车同时起步,从新加坡起程,碰着其他一样部车晚尽管朝着相反的大势重回去飞,
固然这么挨个以片部列车里面往来地飞,直到片辆火车相遇。请问,这仅小鸟一起飞行了大半添加的离开?

其中user_mode(regs)函数,通过判断CPSR寄存器假设用户情势则赶回回0,否则回正数.

呵呵,其实就是是单日子问题万分的题材呀!

据此大家上一致章节的不当的应用程序便会调用__do_user_fault()函数

   
7.君闹半点单罐子以及50只红弹球和50独青色弹球,随机选出一个罐,
随机选出一个弹球放入罐子,怎么样让有肉色弹球最深的入选机会?在你的计划里,拿到红球的几乎率是多少?
 

 

未解

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

   
8.怀想像你站于眼镜前,请问,为啥镜子中之印象可左右倒,却非可以上下颠倒呢? 

 哲学原理 4

平面成像原理呗!

打达到图来拘禁,要惦念打印应用程序的错误音信,还欲:

9.假诺你出管根本多之历届,一个3公升底提捅,一个5公升的提捅,两就提
捅形状上下都不备匀,问您怎么才会可靠称出4公升的道?

3.1布置基本,设置宏CONFIG_DEBUG_USER(只要宏是以“CONFIG_”起,都是暨安排相关)

5-3=2

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

5-(3-2)=4

 哲学原理 5

   
10.若生同一桶果冻,其中起风流、红色、褐色三栽,闭上眼睛抓取同种颜色
的星星点点独。抓取多少坏就是足以规定你肯定有少个相同颜色的果冻? 

故此用Kernel hacking-> Verbose user fault
messages 置为Y,并还烧内核

抽屉原理

3.2使if
(user_debug & UDBG_SEGV)为真

 

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

   
11.老是整数的与也1000的共有几组? 

 哲学原理 6

率先1000呢一个除掉。

引人注目当uboot传递进入的通令行字符里含有”user_debug=”时,便会调用user_debug_setup()->get_option(),最后会将”user_debug=”前边带的字符串提取给user_debug变量.

老是数之平均值设为x,1000须要是x的整数倍增。
假如连续数的个数为突发性数个,x就不是整数了。x的2倍只可以是5,25,125才实施。因为平均值为12.5,要连续80个达标不交。125/262.5凡是足以
的。即62,63,61, 64,等等。

依据:当令行字符里含有”user_debug=0xff”时,则user_debug变量等于0xff

连天数的个数为奇数时,平均值为整数。1000乎平均值的奇数倍增。
1000=2×2×2×5×5×5;x可以为2,4,8,40,200败后剩下40和200凡是
可以的。所以答案为平均值为62.5,40,200,1000底4组整数。

2)其中UDBG_SEGV定义如下所示:

 

#define UDBG_UNDEFINED  (1 << 0)        //用户态的代码出现未定义指令(UNDEFINED)

#define UDBG_SYSCALL (1 << 1)           //用户态系统调用已过时(SYSCALL)     

#define UDBG_BADABORT    (1 << 2)       //用户态数据错误已中止(BADABORT) 

#define UDBG_SEGV     (1 << 3)         //用户态的代码出现段错误(SEGV)

#define UDBG_BUS       (1 << 4)        //用户态访问忙(BUS)

   
12.起同地址出发的同型号的飞行器,不过每架飞机诈满油只可以绕地球飞半周详,飞机里可加油,加完油的机必须重返起源。问至少要小架次,才会知足来相同绑架绕地球一圆。  

打点的定义分析得出,我们只有待将user_debug设为0xff,下面的有着规则就还创建.

 
答案是5架次。一般的解法可以分成如下四只有:
     (1)直线飞行     
相同架飞机载满油飞行距离为1,n架飞机最远能飞多少路程?存在的极值问题是永不再一次飞行,比如简单胁制飞行器还要被同样绑架飞机加油都同时飞回到即可认为是又,或者换句话说,离出发点越远,在奇怪的飞机就愈少,这一个极值条件是明确的,因为n架飞机带的喷漆是必然之,如再,则浪费的油漆就越是多。比如最终一定是可是出平等威吓飞机全程飞行,注意“全程”这一点儿个
字,也固然是无须还的极值条件。如假使片威迫飞行器的语句,肯定是同样绑架为任何一样架加满油,并要剩下的油刚好会返,就说第二绑架飞机带的油耗在3加倍于由出发到加油的
路程上,有三绑架飞机第三绑架带的油耗在5加倍于由出发到其加油的里程达,所以n架飞机最远能飞行之去吗s
1+1/3+?
+1/(2n+1)这么些级数是散的,所以理论及使飞机充裕多最终可以如若一架飞行器飞至无穷远,当然实际上不容许同恫吓飞机于飞行1/(2n+1)时间外与
时被n-1独飞机加油。  
     (2)能够迎头接应加油    
一威吓飞机载满油飞行距离呢1/2,最少几绑架飞行器能飞行距离1?也是按照不要再度飞行之极值条件,得出最远处肯定是可是来一致架飞行器飞,这样得出由1/2处对准
称两止1/4自然是一致绑架飞行器飞,用地点的公式即可知道一边至少要简单劫持飞机辅助,(1/3+1/5)/2>1/4(左侧除为2凡千篇一律架飞行器飞行距离吗1/2),不过发生一点点剩余,所以想像吧一个滑轮(中间一个机是独绳
子,两边两恫吓飞行器是单全)的话,可以滑动一点相距,就说加油地方可以于自然去内改变(很爱算出来每架飞机的加油地方和加油数量,等等)
 

据:当用户态的代码出现无定义指令时,由于user_debug最低位=1,所以打印出oops.

 

从而,进入uboot,在uboot命令行里添加: “user_debug=0xff”

 

4.
起动水源,试验

如下图所示,执行错误的应用程序,只打印了一一寄存器值,以及函数调用关系,而尚未仓库消息:

 哲学原理 7

 

5.接下来,继续修改内核,使应用程序的oops也打印栈音讯出来

于让之oops里发”Stack:
“这个字段,搜索”Stack:
“看看,位于哪个函数

5.1假若下图所著,
找到在__die()函数中:

 哲学原理 8

这个__die()会被die()调用,die()又会被__do_kernel_fault()调用,而我辈应用程序调用的__do_user_fault()里没die()函数,所以并未打印出Stack栈消息。

上图里dump_mem():

dump_mem("Stack: ", regs->ARM_sp,THREAD_SIZE + (unsigned long)task_stack_page(tsk));    //打印stack栈信息

 重假使透过sp寄存器里存的栈地址,每打印一个栈地址里之32员数据,
栈地址便加4(一个地方存8号,所以加4)。

连通下去大家虽因而是原理,来改应用程序调用的__do_user_fault()

 

5.2 在__do_user_fault(),添加以下带红的配:

static
void  __do_user_fault(struct task_struct *tsk, unsigned long
addr,unsigned int fsr, unsignedint
sig, int code,struct pt_regs *regs)

{

       struct siginfo
si;

       unsigned long val ;

       int i=0;

#ifdef
CONFIG_DEBUG_USER

       if (user_debug &
UDBG_SEGV) {

             
printk(KERN_DEBUG “%s: unhandled page fault (%d) at 0x%08lx, code
0x%03x\n”,

                    
tsk->comm, sig, addr, fsr);

             
show_pte(tsk->mm, addr);

             
show_regs(regs);

        printk(“Stack: \n”);

       
while(i<1024)

        {

               /*
copy_from_user()只是用来检测该地址是否管用,如中,便拿走地址数据,否则break
*/

          
if(copy_from_user(&val, (const void __user
*)(regs->ARM_sp+i*4), 4))

           break;

            printk(“%08x
“,val);    //打印数据

            i++;

           
if(i%8==0)

           
printk(“\n”);

        }

        printk(“\n END
of Stack\n”);

      
}

#endif

      
tsk->thread.address = addr;

      
tsk->thread.error_code = fsr;

      
tsk->thread.trap_no = 14;

       si.si_signo =
sig;

       si.si_errno =
0;

       si.si_code =
code;

       si.si_addr =
(void __user *)addr;

      
force_sig_info(sig, &si, tsk);

}

 

6.重烧写内核,试验

正如图所示:

 哲学原理 9

连着下,便来分析PC值,Stack栈,到底怎样调用的

 

7.第一来分析PC值,确定错误的代码

1)生成反汇编:

arm-linux-objdump -D test_debug > test_debug.dis

 

2)搜索PC值84ac,如下图所示:

哲学原理 10

从者看出,重如果将0x12(r3)放入地址0x00(r2)中

而0x00凡个地下地址,所以出错

 

8.分析Stack栈消息,确定函数调用过程

参考: 37.Linux让调试-依照oops的仓库信息,确定函数调用过程

8.1分析过程中,碰着main()函数的回到地址为:LR=40034f14

根本的虚拟地址是c0004000~c03cebf4,而反汇编里也未曾拖欠地点,所以登时是单动态库的地址.

要以静态链接方法,接下去还编译,反汇编,运行:

#arm-linux-gcc -o -static  test_debug test_debug.c
          //-static   静态链接,生成的文件会非常大, 好处在于不需要动态链接库,也可以运行
#arm-linux-objdump -D test_debug > test_debug.dis

8.2末尾,
找到main()函数的返地址在__lobc_start_main()里

于是函数出错时之调用过程:

 __lobc_start_main()->
    main()->
    A()->
        B()->
            C()  //将0x12(r3)放入地址0x00(r2)中

 

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图