Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1599406
  • 博文数量: 92
  • 博客积分: 2002
  • 博客等级: 大尉
  • 技术积分: 4717
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-01 17:09
文章分类

全部博文(92)

文章存档

2013年(1)

2012年(6)

2011年(85)

分类: LINUX

2011-05-27 09:17:11

前一阵子搞动态替换内核函数,过程十分痛苦,特写此文,纪念那些该死的Oops

一、源代码文件

forward.c  驱动模块的文件
  1. #define CODESIZE 4

  2. int (* orig_rtc_dev_open) (struct inode *inode, struct file *file) =
  3.     ( int(*)(struct inode *inode, struct file *file))0xc01f5f00;

  4. void* my_memcpy (void *dest, const void *src, int size)
  5. {
  6.     const char *p = src;
  7.     char *q = dest;
  8.     int i;
  9.     for (i=0; i<size; i++) *q++ = *p++;
  10.     return dest;
  11. }

  12. int my_rtc_dev_open(struct inode *inode, struct file *file)
  13. {
  14.     printk("leonlalal\n");
  15.     return 0;
  16. }

  17. int forward_init(void)
  18. {
  19.     uint32_t addr = 0;
  20.     int i;
  21.     char rtc_dev_open_buf[CODESIZE];

  22.     addr = (uint32_t)my_rtc_dev_open;

  23.     addr = (addr<<2) & ~0xfe000003 ;

  24.     addr |= 0x48000000;
  25.     printk("fuc %08x\n", addr);

  26.     lock_kernel();

  27.     for(i = 0; i< CODESIZE; i++){
  28.         printk("%08x \n", *((uint32_t *)orig_rtc_dev_open + i));
  29.     }

  30.     //kernel code ---> buf
  31.     my_memcpy(rtc_dev_open_buf, (char *)orig_rtc_dev_open, CODESIZE);


  32.     printk("rtc_dev_open_buf[%08x]\n", *((uint32_t *)rtc_dev_open_buf));

  33.     //new jump code ---> kernel code
  34.     my_memcpy(orig_rtc_dev_open, (char*)addr, CODESIZE);

  35.     unlock_kernel();
  36.     return 0;
  37. }

  38. void forward_exit (void)
  39. {
  40.     //buf ---> kernel code
  41.     my_memcpy(orig_rtc_dev_open, rtc_dev_open_buf, CODESIZE);
  42. }
二、看Oops

加载后通过ioctl执行forward_init函数,出现oops
  1. -bash-3.2# dmesg -c
  2. dbg exit now
  3. the major device No. is 215
  4. fuc 4818a900
  5. 9421ffe0
  6. 7c0802a6
  7. 90010024
  8. bfa10014
  9. rtc_dev_open_buf[9421ffe0]
  10. Oops: kernel access of bad area, sig: 11 [#1]
  11. PREEMPT
  12. NIP: f1062a28 LR: f1062b0c CTR: 00000004
  13. REGS: efec5de0 TRAP: 0300 Not tainted (2.6.24.2)
  14. MSR: 00029000 CR: 44000422 XER: 00000000
  15. DEAR: 4818a900, ESR: 00000000
  16. TASK = efea7aa0[1243] 'my_dump' THREAD: efec4000
  17. GPR00: f1062afc efec5e90 efea7aa0 c01f5f00 4818a900 00000004 00000000 c033d130   第三步
  18. GPR08: 00000000 c01f5f00 00000000 c0340000 24000482 100191e8 00000240 00000000
  19. GPR16: 1010f6b0 1009ea94 100f0000 100f7ac8 00000000 1010f540 100ff0b8 00000000
  20. GPR24: 3000e86c 00000002 7fefbc24 0000002e 4818a900 f1060000 f1060000 f1060000
  21. NIP [f1062a28] my_memcpy+0x10/0x28 [enetdbg]      第二步
  22. LR [f1062b0c] forward_init+0xa0/0xbc [enetdbg]
  23. Call Trace:
  24. [efec5e90] [f1062afc] forward_init+0x90/0xbc [enetdbg] (unreliable)      第一步
  25. [efec5eb0] [f106011c] dbg_ioctl+0x11c/0x148 [enetdbg]
  26. [efec5ec0] [c0086d04] do_ioctl+0x84/0xc0
  27. [efec5ee0] [c0086df8] vfs_ioctl+0xb8/0x448
  28. [efec5f10] [c00871c8] sys_ioctl+0x40/0x74
  29. [efec5f40] [c0002340] ret_from_syscall+0x0/0x3c
  30. Instruction dump:
  31. 409dffac 387a45c0 48000161 80010034 bb210014 38210030 7c0803a6 4e800020
  32. 2c050000 7c691b78 4c810020 7ca903a6 <88040000> 38840001 98090000 39290001    第四步
  33. -bash-3.2#

1. 先看Call Trace, 知道是死在了forward_init()

2. 再看NIP(Next Instruction Pointer):
   出错时,下一个执行令的地址为
    [f1062a28] my_memcpy+0x10/0x28 [enetdbg] 
 
   [f1062a28] 出错指令的绝对地址
   my_memcpy+0x10/0x28  出错指令在my_memcpy函数中的偏移量为0x10, 函数总长度为0x28
   [enetdbg] 出错指令的所属模块

3. 看看GPR(General Purpose Register):
   通用寄存器,其中 r3-r10是用来放形参的
        r3 c01f5f00    对应第一个形参 orig_rtc_dev_open,存放指令的地址
        r4 4818a900    对应第二个形参 addr ,即新指令的值,通过打印信息确定 //错误就在这
        r5 00000004    对应第三个形参 CODESIZE


现在基本确认是死在了 forward_init() ---> my_memcpy() 里


4. 死在了哪条语句上呢,就得看 Instruction dump
   我们看到执行的最后几条指令是 <88040000> 38840001 98090000 39290001
 
怎么找这几条指令的对应的C代码呢,就需要反汇编了

三、反汇编找C代码

前面forwar.c 编译后生成了 forward.o
对其进行反汇编 ppc/85xx/bin/ppc_85xx-objdump -S forwar.o > forward_debug.txt
看看forward_debug.txt的内容

  1. forward.o: 文件格式 elf32-powerpc

  2. 反汇编 .text 节:

  3. 00000000 <my_memcpy>:
  4. {
  5.     const char *p = src;
  6.     char *q = dest;
  7.     int i;
  8.     for (i=0; i<size; i++) *q++ = *p++;
  9.    0:    2c 05 00 00     cmpwi r5,0
  10.    4:    7c 69 1b 78     mr r9,r3
  11.    8:    4c 81 00 20     blelr
  12.    c:    7c a9 03 a6     mtctr r5
  13.   10:    88 04 00 00     lbz r0,0(r4)
  14.   14:    38 84 00 01     addi r4,r4,1
  15.   18:    98 09 00 00     stb r0,0(r9)
  16.   1c:    39 29 00 01     addi r9,r9,1
  17.   20:    42 00 ff f0     bdnz+ 10 <my_memcpy+0x10>
  18.     return dest;
  19. }
  20.   24:    4e 80 00 20     blr

  21. 00000028 <my_rtc_dev_open>:

  22. int my_rtc_dev_open(struct inode *inode, struct file *file)
  23. {
  24.   28:    94 21 ff f0     stwu r1,-16(r1)
  25.     printk("leonlalal\n");
  26.   2c:    3c 60 00 00     lis r3,0
  27.   30:    7c 08 02 a6     mflr r0
  28.   34:    38 63 00 00     addi r3,r3,0
  29.   38:    90 01 00 14     stw r0,20(r1)
  30.   3c:    48 00 00 01     bl 3c <my_rtc_dev_open+0x14>
  31.     return 0;
  32. }
  33.   40:    80 01 00 14     lwz r0,20(r1)
  34.   44:    38 60 00 00     li r3,0
  35.   48:    38 21 00 10     addi r1,r1,16
  36.   4c:    7c 08 03 a6     mtlr r0
  37.   50:    4e 80 00 20     blr

  38. 00000054 <forward_init>:

  39. int forward_init(void)
  40. {
  41.   54:    94 21 ff e0     stwu r1,-32(r1)
  42.     uint32_t addr = 0;
  43.     int i;

  44.     addr = (uint32_t)my_rtc_dev_open;
  45.   58:    3d 20 00 00     lis r9,0

  46.     addr = (addr<<2) & ~0xfe000003 ;

  47.     addr |= 0x48000000;
  48.     printk("fuc %08x\n", addr);
  49.   5c:    3c 60 00 00     lis r3,0
  50.   60:    7c 08 02 a6     mflr r0
  51.   64:    38 63 00 0c     addi r3,r3,12
  52.   68:    bf 81 00 10     stmw r28,16(r1)
  53.   6c:    3b 89 00 00     addi r28,r9,0
  54.   70:    57 9c 11 fa     rlwinm r28,r28,2,7,29
  55.   74:    90 01 00 24     stw r0,36(r1)
  56.   78:    67 9c 48 00     oris r28,r28,18432

  57.     lock_kernel();

  58.     for(i = 0; i< CODESIZE; i++){
  59.   7c:    3b a0 00 00     li r29,0
  60.   80:    7f 84 e3 78     mr r4,r28
  61.   84:    4c c6 31 82     crclr 4*cr1+eq
  62.   88:    48 00 00 01     bl 88 <forward_init+0x34>
  63.   8c:    3f c0 00 00     lis r30,0
  64.   90:    48 00 00 01     bl 90 <forward_init+0x3c>
  65.   94:    3f e0 00 00     lis r31,0
  66.         printk("%08x \n", *((uint32_t *)orig_rtc_dev_open + i));
  67.   98:    81 7f 00 00     lwz r11,0(r31)
  68.   9c:    57 a9 10 3a     rlwinm r9,r29,2,0,29
  69.   a0:    38 7e 00 18     addi r3,r30,24
  70.   a4:    3b bd 00 01     addi r29,r29,1
  71.   a8:    7c 89 58 2e     lwzx r4,r9,r11
  72.   ac:    4c c6 31 82     crclr 4*cr1+eq
  73.   b0:    48 00 00 01     bl b0 <forward_init+0x5c>
  74.   b4:    2f 9d 00 03     cmpwi cr7,r29,3
  75.   b8:    40 9d ff e0     ble+ cr7,98 <forward_init+0x44>
  76.     }

  77.     //kernel code ---> buf
  78.     my_memcpy(rtc_dev_open_buf, (char *)orig_rtc_dev_open, CODESIZE);
  79.   bc:    80 9f 00 00     lwz r4,0(r31)
  80.   c0:    3f a0 00 00     lis r29,0
  81.   c4:    38 a0 00 04     li r5,4
  82.   c8:    38 7d 00 00     addi r3,r29,0
  83.   cc:    48 00 00 01     bl cc <forward_init+0x78>


  84.     printk("rtc_dev_open_buf[%08x]\n", *((uint32_t *)rtc_dev_open_buf));
  85.   d0:    80 9d 00 00     lwz r4,0(r29)
  86.   d4:    3c 60 00 00     lis r3,0
  87.   d8:    38 63 00 20     addi r3,r3,32
  88.   dc:    4c c6 31 82     crclr 4*cr1+eq
  89.   e0:    48 00 00 01     bl e0 <forward_init+0x8c>

  90.     //new jump code ---> kernel code
  91.     my_memcpy(orig_rtc_dev_open, (char*)addr, CODESIZE);
  92.   e4:    80 7f 00 00     lwz r3,0(r31)
  93.   e8:    7f 84 e3 78     mr r4,r28
  94.   ec:    38 a0 00 04     li r5,4
  95.   f0:    48 00 00 01     bl f0 <forward_init+0x9c>

  96.     unlock_kernel();
  97.   f4:    48 00 00 01     bl f4 <forward_init+0xa0>
  98.     return 0;
  99. }
  100.   f8:    80 01 00 24     lwz r0,36(r1)
  101.   fc:    38 60 00 00     li r3,0
  102.  100:    bb 81 00 10     lmw r28,16(r1)
  103.  104:    7c 08 03 a6     mtlr r0
  104.  108:    38 21 00 20     addi r1,r1,32
  105.  10c:    4e 80 00 20     blr

  106. 00000110 <forward_exit>:

  107. void forward_exit (void)
  108. {
  109.     //buf ---> kernel code
  110.     my_memcpy(orig_rtc_dev_open, rtc_dev_open_buf, CODESIZE);
  111.  110:    3d 20 00 00     lis r9,0
  112.  114:    3c 80 00 00     lis r4,0
  113.  118:    80 69 00 00     lwz r3,0(r9)
  114.  11c:    38 84 00 00     addi r4,r4,0
  115.  120:    38 a0 00 04     li r5,4
  116.  124:    48 00 00 00     b 124 <forward_exit+0x14>

来搜 <88040000> 38840001 98090000 39290001 来这几条指令
原来是死在了my_memcpy里的赋值语句上。
赋值操作应该没有错,那就是my_memcpy 形参错了,最后定位到
  1.     //new jump code ---> kernel code
  2.     my_memcpy(orig_rtc_dev_open, (char*)addr, CODESIZE);
我擦,addr丢了一个&,改之
         my_memcpy(orig_rtc_dev_open, (char*)&addr, CODESIZE);

---------------------------------------------------------------------------------------------

后话:
编译,运行,那就是另外一个Oops了,不是死在自己的模块函数里了,死在内核函数里了。
反汇编找C代码的时候更麻烦了,不过套路是一样的,反汇编内核就
ppc/85xx/bin/ppc_85xx-objdump -S vmlinux | less

有些时候,Oops的Instruction dump里全是xxxx,那你就得详细的查查看LR,CTR什么的是不是错了,多半你的代码跳到了一个非法的地址。

四、arm的oops调试(没地儿放,就这了吧)


点击(此处)折叠或打开

  1. [root@leonwang]# mmcconfig -a
  2. Unable to handle kernel NULL pointer dereference at virtual address 00000048
  3. pgd = dfbac000
  4. [00000048] *pgd=1fadb831, *pte=00000000, *ppte=00000000
  5. Internal error: Oops: 817 [#1] PREEMPT SMP
  6. Modules linked in: mmc_drv(O)
  7. CPU: 0 Tainted: G O (3.3.0-14.2-build1 #58)
  8. PC is at xilinx_prepare_hs_sdr+0x30/0xb8
  9. LR is at xilinx_prepare_hs_sdr+0x28/0xb8
  10. pc : [<c02a74d8>] lr : [<c02a74d0>] psr: 60000013
  11. sp : dfae3ec0 ip : dfae2000 fp : 00000000
  12. r10: 00000000 r9 : dfae2000 r8 : 00000000
  13. r7 : dfbed400 r6 : 00000000 r5 : df871280 r4 : df871000
  14. r3 : 00000000 r2 : 00000000 r1 : 017d7840 r0 : 00000006
  15. Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
  16. Control: 18c5387d Table: 1fbac04a DAC: 00000015
  17. Process mmcconfig (pid: 555, stack limit = 0xdfae22f0)
  18. Stack: (0xdfae3ec0 to 0xdfae4000)
  19. 3ec0: bef41d20 df871000 dfae2000 bf000b0c df030dac dfae3f78 00000001 df9cd000
  20. 3ee0: ffffff9c c0015d28 00000000 00000007 c04c6084 b6e9a670 00000006 b6f39000
  21. 3f00: df5704a8 bef41d20 dfa68a40 00000003 c0015d28 c00a9760 00005452 c00aa1e0
  22. 3f20: 00000000 00000000 df993240 0000001b 00000000 c008a678 dfae13c8 00000000
  23. 3f40: 00000000 00000000 00000002 00000003 00000003 c0099b98 00000000 00000003
  24. 3f60: 00000003 dfa68a40 bef41d20 c0084502 00000003 c0015d28 dfae2000 00000000
  25. 3f80: 00000000 c00aa25c 00000003 00000000 bef41d20 00015a3c 00015a1c 00015a28
  26. 3fa0: 00000036 c0015b80 00015a3c 00015a1c 00000003 c0084502 bef41d20 00015a60
  27. 3fc0: 00015a3c 00015a1c 00015a28 00000036 00000000 00000000 00015a34 00000000
  28. 3fe0: bef41e94 bef41d18 0000c150 b6e9a67c 60000010 00000003 f7fff3ff 6f7ddfff
  29. [<c02a74d8>] (xilinx_prepare_hs_sdr+0x30/0xb8) from [<bf000b0c>] (emmc_test_ioctl+0x210/0x2e4 [mmc_drv])
  30. [<bf000b0c>] (emmc_test_ioctl+0x210/0x2e4 [mmc_drv]) from [<c00a9760>] (vfs_ioctl+0x24/0x40)
  31. [<c00a9760>] (vfs_ioctl+0x24/0x40) from [<c00aa1e0>] (do_vfs_ioctl+0x4cc/0x514)
  32. [<c00aa1e0>] (do_vfs_ioctl+0x4cc/0x514) from [<c00aa25c>] (sys_ioctl+0x34/0x54)
  33. [<c00aa25c>] (sys_ioctl+0x34/0x54) from [<c0015b80>] (ret_fast_syscall+0x0/0x30)
  34. Code: 0a000000 e12fff33 e3a00006 e59f1074 (e5860048)
  35. ---[ end trace 901cac0a83592b19 ]---
  36. Segmentation fault
  37. [root@leonwang]#

看来是内核build-in 驱动函数xilinx_prepare_hs_sdr() 出bug了


  1. #arm-linux-objdump -S sdhci_xilinx.o >> tmp_file
  2. #cat tmp_file
  3.  ..........

  4.  710 00000a7c <xilinx_prepare_hs_sdr>:
  5.  711 a7c: e92d4070 push {r4, r5, r6, lr}
  6.  712 a80: e1a04000 mov r4, r0
  7.  713 a84: e2805d0a add r5, r0, #640 ; 0x280
  8.  714 a88: e5906290 ldr r6, [r0, #656]
  9.  715 a8c: f57ff04f dsb sy
  10.  716 a90: e59f3088 ldr r3, [pc, #136] ; b20 <xilinx_prepare_hs_sdr+0xa4>
  11.  717 a94: e5933018 ldr r3, [r3, #24]
  12.  718 a98: e3530000 cmp r3, #0 ; 0x0
  13.  719 a9c: 0a000000 beq aa4 <xilinx_prepare_hs_sdr+0x28>
  14.  720 aa0: e12fff33 blx r3
  15.  721 aa4: e3a00006 mov r0, #6 ; 0x6
  16.  722 aa8: e59f1074 ldr r1, [pc, #116] ; b24 <xilinx_prepare_hs_sdr+0xa8>
  17.  723 aac: e5860048 str r0, [r6, #72]
  18.  724 ab0: e1a00004 mov r0, r4
  19.  725 ab4: ebfffffe bl 2a0 <xilinx_set_clk>
  20.  726 ab8: e3a0400b mov r4, #11 ; 0xb
  21.  727 abc: ea000001 b ac8 <xilinx_prepare_hs_sdr+0x4c>
  22.  728 ac0: e59f0060 ldr r0, [pc, #96] ; b28 <xilinx_prepare_hs_sdr+0xac>
  23.  729 ac4: ebfffffe bl 0 <__const_udelay>
  24.  730 ac8: e2544001 subs r4, r4, #1 ; 0x1
  25.  731 acc: 1afffffb bne ac0 <xilinx_prepare_hs_sdr+0x44>

找到 xilinx_prepare_hs_sdr+0x30 处
xilinx_prepare_hs_sdr           是 00000a7c
xilinx_prepare_hs_sdr+0x30  是 00000aac

00000aac处指令为 e5860048, 与上面oops日志
Code: 0a000000 e12fff33 e3a00006 e59f1074 (e5860048)
相对应


下面对应的是驱动中的C代码

点击(此处)折叠或打开

  1. 42 #define XILINX_EMMC_DMA_RD_LEN 0x40
  2.   43 #define XILINX_EMMC_DMA_WR_LEN 0x44
  3.   44 #define XILINX_EMMC_CFG 0x48

  4.   ........

  5.   87 //XXX "XILINX_EMMC_CFG" field
  6.   88
  7.   89 #define CFG_DDR_ENABLE 0x00000001 // 0.SDR 1.DDR
  8.   90 #define CFG_DATA_CLK 0x00000002 // 0.400k 1.data transfer clock
  9.   91 #define CFG_DMA_ENABLE 0x00000004 // 0.disable 1.enable
  10.   92 #define CFG_HS400_ENABLE 0x00000008 // 0.disable 1.enable

  11.   ........

  12.  418 void xilinx_prepare_hs_sdr(struct mmc_host *mmc)
  13.  419 {
  14.  420 struct xilinx_emmc_host *host = mmc_priv(mmc);
  15.  421 uint8_t *base_reg = host->base_reg;
  16.  422 uint32_t tmp = 0;
  17.  423 int i = 0;
  18.  424
  19.  425 // xilinx host enter hs sdr mode
  20.  426 if( xilinx_use_dma )
  21.  427 tmp |= CFG_DMA_ENABLE;
  22.  428
  23.  429 writel( tmp | CFG_DATA_CLK, base_reg + XILINX_EMMC_CFG);
  24.  430
  25.  431 xilinx_set_clk(mmc, 25000000); // 25MHz
  26.  432 mdelay(10);
  27.  433
  28.  434
  29.  435 // xilinx host only support 8-bit bus width(single date rate)
  30.  436 tmp = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | // write
  31.  437 (EXT_CSD_BUS_WIDTH << 16) | // index
  32.  438 (2 << 8); // value
  33.  439
  34.  440 xilinx_send_pri_cmd(host,MMC_SWITCH, tmp , MMC_RSP_R1);
  35.  441
  36.  442 for(i = 0; i < XILINX_RETRY_MAX; i++){
  37.  443 tmp = xilinx_send_pri_cmd(host,MMC_SEND_STATUS, 0x00010000, MMC_RSP_R1 );
  38.  444 if(R1_CURRENT_STATE(tmp) != R1_STATE_PRG)
  39.  445 break;
  40.  446 }
  41.  447
  42.  448 if(i == XILINX_RETRY_MAX)
  43.  449 printk("send pri switch EXT_CSD_BUS_WIDTH error [%08x]\n", tmp);
  44.  450
  45.  451 return ;
  46.  452 }

汇编中的

  1. 721 aa4: e3a00006 mov r0, #6 ; 0x6
  2.  722 aa8: e59f1074 ldr r1, [pc, #116] ; b24 <xilinx_prepare_hs_sdr+0xa8>
  3.  723 aac: e5860048 str r0, [r6, #72]

对应C中的

  1. 425 // xilinx host enter hs sdr mode
  2.  426 if( xilinx_use_dma )
  3.  427 tmp |= CFG_DMA_ENABLE;
  4.  428
  5.  429 writel( tmp | CFG_DATA_CLK, base_reg + XILINX_EMMC_CFG);

因为tmp最终值为6  ( 宏 CFG_DMA_ENABLE | CFG_DATA_CLK的值为6)
宏XILINX_EMMC_CFG 的值为0x48
汇编代码中 其实执行的是 writel(6,0x48)
即base_reg为0,异常。 排查得知xilinx_prepare_hs_sdr传入的形参错误,导致host指针错误

阅读(7230) | 评论(1) | 转发(6) |
给主人留下些什么吧!~~

dongyang6262012-02-24 15:13:19

这篇文章很久前就看到了..不过由于网络原因,我是将文章保存到本地阅读的..
最近也在调试oops.   内核里面的,表现有点灵异,小弟菜鸟,望指导。
不知能否邮件联系,也好详细说明问题, 给你发纸条了哈。  非常感谢!