Chinaunix首页 | 论坛 | 博客
  • 博客访问: 51793
  • 博文数量: 36
  • 博客积分: 2000
  • 博客等级: 大尉
  • 技术积分: 390
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-23 15:42
文章分类
文章存档

2011年(1)

2009年(35)

我的朋友
最近访客

分类: LINUX

2009-08-21 15:13:38

   今天写了个PodDriver被一个couldn't find the kernel version the module was compiled for错误折腾了一上午,汗!在网上找答案然而网上都说是版本不匹配问题,然而我的版本百分之120是一个版本的。我反复猜测,此问题是在insmod 时候出现的错误,insmod的时候是调用init函数,然后我反复检查init函数,毫无收获,最后想那么这句话是在哪里打印的呢,就上网查了insmod.c 文件红色地方就是错误打印出来的信息。那么好了,就检查一下自己定义module定义处,果然我的程序出错了,
#ifndef MODULE
   #define MODULE
 
kernel与module的版本问题.

应该先看看linux/module.h文件,然后再看insmod.c中关于这一段的描写
**********************************************************************************
   ............
k_version = get_kernel_version(k_strversion);   //[/Color]
顾名,得到kernel的版本号.[/Color]
m_version = get_module_version(f, m_strversion);  //[/Color]如果defined (modules) & !defined(__GENKSYMS__),则该数值来自模块的头文件(如/usr/include/linux/version.h或者你用-I 选项指定的头文件目录中的linux/version.h中的UTS_RELEASE)如果没有定义....,(见(1))[/Color]
if (m_version == -1) {        
  error("couldn't find the kernel version the module was compiled for");//[/Color]直接跳出,没有#define MODULE的时候会发生这种情况[/Color]
  goto out;
}

k_crcs = is_kernel_checksummed();//[/Color]内核引出的符号中搜索一个名为"Using_Versions"的符号,并返回它的值,多半为1(cat /proc/ksyms |grep -i using_versions)[/Color]
m_crcs = is_module_checksummed(f);     //[/Color]见(3),如#define MODVERSIONS则搜模块的.modinfo节中的"using_checksums"或者为1或者为0[/Color]
if ((m_crcs == 0 || k_crcs == 0) && strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0)
{
  [/Color](a)从这个if语句可见如果内核或者模块只要有一方或者都没有包含R*********的检验的话,就要比较模块版本和内核版本是否一致.这很合理,因为如果他们都支持版本检验的话,就会在要寻找符号的时候用严格的strcmp函数,所以就不用在这里比较版本号了(例如模块中要用到printk函数,该函数是内核引出的一个符号.insmod就要负责在内核的资源里面把这个符号(函数)找出来,将它的地址填入模块的相应位置,而寻找的过程正是一个比较的过程,strcmp是很严格的比较,而相应的不严格的比较是用函数ncv_strcmp完成的它忽略printk后面的R******)
  (b)模块与内核版本不一致的话就要看编译时候是否你选择了-f标志
  (c)另外我们可以从后面linux/module.h看出在defined (modules) & !defined(__GENKSYMS__)的条件下,如果我们#define MODVERSIONS,那么m_crss就是1(k_crss一般都是1),这样我们不管你的include文件中的版本号和内核版本号是否一致都可以跳过现在的这个版本检验.当然以后符号解析时就不一定能过关了.[/Color]

  if (flag_force_load) {       //[/Color]insmod -f *.o,强制注入模块[/Color]
   lprintf("Warning: kernel-module version mismatch\n"
         "\t%s was compiled for kernel version %s\n"
    "\twhile this kernel is version %s",
    filename, m_strversion, k_strversion);
  } else {
   if (!quiet)
    error("kernel-module version mismatch\n"
          "\t%s was compiled for kernel version %s\n"
          "\twhile this kernel is version %s.",
          filename, m_strversion, k_strversion);
   goto out;       //[/Color]毅然退出[/Color]
  }
}
if (m_crcs != k_crcs)      //[/Color]如果二者不等,说明kernel与模块至少有一方不检验R********[/Color]
  obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash); //[/Color]那么就不用strcmp(),而用ncv_strcmp()比较,它忽略R***使得printk和printk_R***一样.[/Color]

/* Let the module know about the kernel symbols.  */
add_kernel_symbols(f);

   ...............
*****************************************************************************************
(1)get_module_version()

/* Get the module's kernel version in the canonical integer form

[/Color]obj_file是一个内存中的结构,它对应objfile(例如我们的模块文件*.o)还有一些额外的信息,obj_file结构由obj_load ()填充,例如把目标文件的所有的节头各个节头
section_headers和部分类型的节,SHT_PROGBITS,SHT_SYMTAB,SHT_STRTAB,SHT_RELM的内容SHT_NULL,NOTE,NOBITS除外),从文件中读到内存中的obj_file结构的相应位置等等.[/Color]
*/

static int get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
{       int a, b, c;
        char *p, *q;
        if ((p = get_modinfo_value(f, "kernel_version")) == NULL) (见(4))
[/Color]在obj_file中的.modinfo节(该节由字符串组成)中搜索"kernel_version=*****"的字样(也有可能是"kernel_version"),并返回****的地址,如果没有"kernel_version"就返回NULL.注意在
obj_load()中去掉了该节的SHF_ALLOC属性,表明该节不占用内存.[/Color]
        {               
                struct obj_symbol *sym;
                m_has_modinfo = 0;    // [/Color] 这是个全局量[/Color]
                if ((sym = obj_find_symbol(f, "kernel_version")) == NULL)
[/Color]如果没有在obj_file的.modinfo节中找到kernel_version就要从obj_file->symtab这张用名字作为hash值的hash表中寻找符号kernel_verrsion或者__module_kernel_version(可能内核引出的)(我觉得不会在本地数组obj_file->local_symtab中找到这两个符号)[/Color]
                        sym = obj_find_symbol(f, "__module_kernel_version");   
                if (sym == NULL)
                        return -1; [/Color]会出现couldn't find the kernel version the module was compiled for然后一路退出安装.[/Color]
                p = f->sections[sym->secidx]->contents + sym->value;
        } else
                m_has_modinfo = 1;
        strncpy(str, p, STRVERSIONLEN);           //[/Color]将版本字符串组成一个数值   [/Color]                  
        a = strtoul(p, &p, 10);
        if (*p != '.')
                return -1;
        b = strtoul(p + 1, &p, 10);
        if (*p != '.')
                return -1;
        c = strtoul(p + 1, &q, 10);
        if (p + 1 == q)
                return -1;
        return a << 16 | b << 8 | c; }

*********************************************************************************************
[/Color]上面的内容尤其是elf文件中的.modinfo节,应该参看linux/module.h中的一些描述.部分列举如下:[/Color]
#if defined(MODULE) && !defined(__GENKSYMS__)
205 #define MODULE_AUTHOR(name)                                                \
206 const char __module_author[] __attribute__((section(".modinfo"))) =        \
207 "author=" name
................
297 #include
298 static const char __module_kernel_version[] __attribute__((section(".modinfo"))) =
299 "kernel_version=" UTS_RELEASE;
300 #ifdef MODVERSIONS
301 static const char __module_using_checksums[] __attribute__((section(".modinfo"))) =
302 "using_checksums=1";     //[/Color]帮助我们避免第一次内核与模块的版本检验.[/Color]
303 #endif
*******************************************************************************************************
(2) is_kernel_checksummed()
[/Color]从内核引出的符号中搜索一个名为"Using_Versions"的符号,并返回它的值[/Color]

  /* Return the kernel symbol checksum version, or zero if not used. */
static int is_kernel_checksummed(void)
{
        struct module_symbol *s;
        size_t i;       /*       * Using_Versions might not be the first symbol, * but it
should be in there.     

                         */
        for (i = 0, s = ksyms; i < nksyms; ++i, ++s)
                if (strcmp((char *) s->name,"Using_Versions") == 0)
                        return s->value;
        return 0;
}  [/Color]
这个函数很简单,值得一说的是ksyms是个全局量,get_kernel_info函数负责取得kernel中所有已经注册的modules,放入module_stat中,并将kernel实现的各个symbol放入ksyms中的.流程大概是get_kernel_info()--->new_get_kernel_info()--->query_module(NULL, QM_SYMBOLS,ksyms, bufsize, &ret))
[/Color]

************************************************************************************************

(3)is_module_checksummed()[/Color]
如果在obj_file的.modinfo节中有"kernel_version",则继续寻找"Using_checksums=***"的***(如果#defin MODVERSIONS的话就为1),反之就在obj_file的hash表symtab中寻找符号"Using_Versions"的数值.[/Color]
static int is_module_checksummed(struct obj_file *f)
{
        if (m_has_modinfo)
        {
                const char *p = get_modinfo_value(f, "using_checksums");
                if (p)
                        return atoi(p);                         else
                        return 0;
        } else
                return obj_find_symbol(f, "Using_Versions") != NULL;
}


************************************************************************************************
        / /* begin compat */
(4)get_modinfo_value()
       [/Color] 在object文件中的.modinfo节中搜索"kernel_version=*****"的字样,并返回****的地址或者是该节中是"kernel_version"这样的字符窜而不是"kernel_version=*****"这样的东西.如果没有"kernel_version"就返回NULL.[/Color]
static char * get_modinfo_value(struct obj_file *f, const char *key)
//例如key="kernel_version"
{      
        struct obj_section *sec;
        char *p, *v, *n, *ep;
        size_t klen = strlen(key);
        sec = obj_find_section(f, ".modinfo");
        if (sec == NULL)
                return NULL;
        p = sec->contents;
        ep = p + sec->header.sh_size;//[/Color]本节(.modinfo)的末尾[/Color]
        while (p < ep)
        {
                v = strchr(p, '=');
                n = strchr(p, '\0');
                if (v)
                {
                        if (v - p == klen && strncmp(p, key, klen) == 0)                                
                                return  v + 1;
                } else
                {
                        if (n - p == klen && strcmp(p, key) == 0)
                                return n;
                }
                p = n + 1;
        }
        return NULL;
}
阅读(1229) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~