Chinaunix首页 | 论坛 | 博客
  • 博客访问: 51817
  • 博文数量: 8
  • 博客积分: 15
  • 博客等级: 民兵
  • 技术积分: 120
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-04 20:32
个人简介

aaaaaaaaaaaaaaaaaaaa

文章分类

全部博文(8)

文章存档

2013年(8)

我的朋友

分类: LINUX

2013-10-24 08:08:35

最近给客户设计了一个Linux驱动,是以Linux Kernel Module方式提供的。
给到客户之后发现无法insmod成功,在insmod的时候发生panic;当时觉得很奇怪,因为这个时候module里面的code还没开始跑,甚至怀疑客户的Linux OS有Bug。
不过,还是跟客户要了Kernel Log分析了一把: 从Panic Log来看是访问了一个非法的指针:
[ 1060.760738] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[ 1060.769057] pgd = c17d8000
[ 1060.771801] [00000000] *pgd=00000000
[ 1060.775487] Internal error: Oops: 5 [#1] PREEMPT SMP
回头去看backtrace
[ 1061.654097] Backtrace:
[ 1061.656573] [] (trace_module_add_events+0x0/0x6c) from []  (trace_module_notify+0x38/0x58)
[ 1061.666498]  r7:c072925c r6:fffffffc r5:bf225cd0 r4:00000001
[ 1061.672239] [] (trace_module_notify+0x0/0x58) from [] (notifier_call_chain+0x48/0x78)
[ 1061.681817]  r5:00000000 r4:00000001
[ 1061.685440] [] (notifier_call_chain+0x0/0x78) from [] (__blocking_notifier_call_chain+0x58/0x70)
[ 1061.695973]  r8:c00172c8 r7:00000001 r6:bf225cd0 r5:ffffffff r4:c07280d0
[ 1061.702567] r3:ffffffff
[ 1061.705225] [] (__blocking_notifier_call_chain+0x0/0x70) from [] (blocking_notifier_call_chain+0x28/0x30)
[ 1061.716540]  r7:00006e94 r6:00000000 r5:bef626b4 r4:bf225cd0
[ 1061.722276] [] (blocking_notifier_call_chain+0x0/0x30) from [] (sys_init_module+0x74/0x1a0)
[ 1061.732381] [] (sys_init_module+0x0/0x1a0) from [] (ret_fast_syscall+0x0/0x48)
从代码分析: mod中的一个指针烂了。
   trace_events.c

点击(此处)折叠或打开

  1. static void trace_module_add_events(struct module *mod)
  2. {
  3.     struct ftrace_module_file_ops *file_ops = NULL;
  4.     struct ftrace_event_call **call, **start, **end;

  5.     start = mod->trace_events;
好像有点眉目了,因为这个module structure是在build module的时候塞进.ko 中的。
在insmod的时候,Linux Kernel会将这个Strucutre取出来进行处理。
具体请参看: 
kernel/module.c

点击(此处)折叠或打开

  1. /* This is where the real work happens */
  2. SYSCALL_DEFINE3(init_module, void __user *, umod,
  3.         unsigned long, len, const char __user *, uargs)
  4. ==>
  5. static struct module *load_module(void __user *umod,
  6.                  unsigned long len,
  7.                  const char __user *uargs)
  8. ==>
  9. static struct module *layout_and_allocate(struct load_info *info)
  10. ==>
  11. static struct module *setup_load_info(struct load_info *info)
  12. ==>
  13. info->index.mod = find_sec(info, ".gnu.linkonce.this_module");
    if (!info->index.mod) {
    printk(KERN_WARNING "No module found in object\n");
    return ERR_PTR(-ENOEXEC);
    }
    /* This is temporary: point mod into copy of data. */
    mod = (void *)info->sechdrs[info->index.mod].sh_addr;

从代码里可以看出,在.ko 文件中,有一个名为 ".gnu.linkonce.this_module" 的Section,里面装着module这个structure本身。
举个例子: 
readelf -S xxxx.ko ,第17个 Section即这个东西。

  1. Section Headers:
  2.   [Nr] Name Type Addr Off Size ES Flg Lk Inf Al
  3.   [16] .rel.init_array REL 00000000 01b7a4 000008 08 45 15 4
  4.   [17] .gnu.linkonce.thi PROGBITS 00000000 0018f0 00014c 00 WA 0 0 4
  5.   [18] .rel.gnu.linkonce REL 00000000 01b7ac 000010 08 45 17 4
  6.   [19] .ARM.exidx ARM_EXIDX 00000000 001a3c 000078 00 AL 1 0 4
另外kernel 还有一个invariable标记这个东西: 
readelf -s xxxx.ko | grep this
   171: 00000000   332 OBJECT  GLOBAL DEFAULT   17 __this_module

据猜测是这个structure出了问题;
最后发现Linux OS中/system/lib/modules里面的结构大小跟这个不一样。
readelf -s 8191su.ko | grep this
  1035: 00000000   336 OBJECT  GLOBAL DEFAULT   22 __this_module

去看linux/include/linux/module.h中的structure ; 发现里面有很多invariable跟Kernel Option有关。

点击(此处)折叠或打开

  1. struct module
  2. {
  3. ...

  4. #ifdef CONFIG_TRACEPOINTS
  5.     unsigned int num_tracepoints;
  6.     struct tracepoint * const *tracepoints_ptrs;
  7. #endif
  8. ...
  9. #ifdef CONFIG_TRACING
  10.     unsigned int num_trace_bprintk_fmt;
  11.     const char **trace_bprintk_fmt_start;
  12. #endif
  13. #ifdef CONFIG_EVENT_TRACING
  14.     struct ftrace_event_call **trace_events;
  15.     unsigned int num_trace_events;
  16. #endif
  17. #ifdef CONFIG_FTRACE_MCOUNT_RECORD
  18.     unsigned int num_ftrace_callsites;
  19.     unsigned long *ftrace_callsites;
  20. #endif
按图索骥,去查kernel option 。结果找到原因是Linux OS本身开启的debug option不一样,最后,改成一样的就没问题了
所以,在Build Linux Kernel Module的时候,用的Linux Version 一定要一致,并且开启的Kernel Option一定要一样,要不然,如果倒霉的话:Build出来的Kernel Module能够安装进去,因为data structure不一致导致memory corruption就非常难查了。


这也是为什么Linux Kernel在insmod的时候会去check magic version(包括内核版本,以及一些重要的Kernel Option,such as, SMP ......)

另外,在网上找到一点资料,指导我们在没有客户source的状况下如何build出正确的module,当然,这也是有条件的:)  ,它的方法是通过 /proc/config.gz 获取kernel 的option ,不过,我的Samsung Galaxy就没有这个东东:( 
http://glandium.org/blog/?p=2664

Building a Linux kernel module without the exact kernel headers


另外,如果Option不一致,还可能导致所用的API不同导致在insmod的时候遇到unresolved symbol.(例如,某些debug的开关会使用不同API;such as: spin lock的debug功能打开和关闭会有很大差异)



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