• modutils及LKM分析小记[2]


    1.

    module结构

    struct module
    {
    enum module_state state; /* 模块的内部状态 */
    struct list_head list; /* 模块链表指针 */
    char name[MODULE_NAME_LEN]; /* 模块名,60字节 */

    struct module_kobject mkobj; /* 包含一个kobject结构 */
    struct module_attribute *modinfo_attrs;
    const char *version;
    const char *srcversion;
    struct kobject *holders_dir;

    const struct kernel_symbol *syms; /* 导出符号数组指针 */
    const unsigned long *crcs; /* 导出符号CRC数组指针 */
    unsigned int num_syms; /* 导出符号个数 */

    struct kernel_param *kp; /* 内核参数描述符数组指针 */
    unsigned int num_kp; /* 内核参数个数 */

    unsigned int num_gpl_syms; /* GPL格式导出符号数 */
    const struct kernel_symbol *gpl_syms; /* GPL格式导出符号数组指针 */
    const unsigned long *gpl_crcs; /* GPL格式导出符号CRC数组指针 */

    const struct kernel_symbol *unused_syms; /* 未使用的导出符号数组指针 */
    const unsigned long *unused_crcs; /* 未使用的导出符号CRC数组指针 */
    unsigned int num_unused_syms; /* 未使用的导出符号数 */

    unsigned int num_unused_gpl_syms; /* 未使用的GPL格式导出符号数 */
    const struct kernel_symbol *unused_gpl_syms; /* 未使用的GPL格式导出符号数组指针 */
    const unsigned long *unused_gpl_crcs; /* 未使用的GPL格式导出符号CRC数组指针 */

    const struct kernel_symbol *gpl_future_syms; /* 保留的GPL格式导出符号数组指针 */
    const unsigned long *gpl_future_crcs; /* 保留的GPL格式导出符号CRC数组指针 */
    unsigned int num_gpl_future_syms; /* 保留的GPL格式导出符号数 */

    unsigned int num_exentries; /* 模块异常表项个数 */
    struct exception_table_entry *extable; /* 模块异常表指针 */

    int (*init)(void); /* 模块初始化函数 */
    void *module_init; /* 用于模块初始化的动态内存指针,由vfree在init()返回时释放 */
    void *module_core; /* 用于模块核心函数与数据结构,由vfree在卸载时释放 */
    unsigned int init_size, core_size; /* 模块初始化与模块核心函数及数据结构的动态内存大小 */
    unsigned int init_text_size, core_text_size; /* 模块初始化与模块核心函数及数据结构的可执行代码大小,只在链接时使用 */

    struct mod_arch_specific arch; /* 依赖于体系结构的字段 */
    unsigned int taints; /* 内核污染标志位 */

    Elf_Sym *symtab, *core_symtab; /* /proc/kallsyms文件模块ELF核心符号数组指针 */
    unsigned int num_symtab, core_num_syms; /* /proc/kallsyms文件模块ELF符号表数与核心符号数 */
    char *strtab, *core_strtab; /* /proc/kallsyms文件模块ELF核心符号字符串指针 */

    struct module_sect_attrs *sect_attrs; /* 模块的段属性描述符数组指针 */

    void *percpu; /* 特定于CPU的内存区指针 */
    char *args; /* 链接模块时的命令行参数 */

    struct list_head modules_which_use_me; /* 依赖于该模块的模块链表 */
    struct task_struct *waiter; /* 等待该模块卸载的进程 */

    void (*exit)(void); /* 模块卸载函数 */
    local_t ref; /* CPU使用计数器 */
    };

    http://blog.csdn.net/cangel1988/article/details/6882207
    http://wanderer-zjhit.blogbus.com/logs/151947468.html
    http://blog.chinaunix.net/space.php?uid=12076195&do=blog&id=229334
    http://hi.baidu.com/linux_kernel/blog/item/7b663ac73b73e3dcd1006093.html

    2.
    而kernel中有一个全局变量module_list为所有的module的list,在系统初始化是module_list只有一项为
    struct module *module_list = &kernel_module;
    kernel_module中的syms为__start___ksymtab即为kernel对外输出的symbol的所有项。

    3.

    int get_kernel_info(int type)
    {
    k_new_syscalls = !query_module(NULL, 0, NULL, 0, NULL);

    #ifdef COMPAT_2_0
    if (!k_new_syscalls)
    return old_get_kernel_info(type);
    #endif /* COMPAT_2_0 */

    return new_get_kernel_info(type);
    }

    k_new_syscalls=!query_module(NULL,0,NULL,0,NULL)用于测试内核的版本,sys_query_module在2.1.x以后才加入的。

    module_names = xmalloc(bufsize = 256);
    while (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) {
    if (errno != ENOSPC) {
    error("QM_MODULES: %m\n");
    return 0;
    }
    module_names = xrealloc(module_names, bufsize = ret);
    }

    从http://os.51cto.com/art/201108/286556.htm可以看出:
    query_module:
    RETURN VALUE
    On success, zero is returned. On error, -1 is returned and errno is set appropriately.
    如果成功,直接返回,如果不成功,进入后利用ENOSPC的ret等于最小需要空间
    ENOSPC
    The buffer size provided was too small. ret is set to the minimum size needed.

    4.

    obj_load函数主要是加载了模块对应的elf文件格式,分析符号段,重定位段,根据符号的属性分别保存在local_symtab和symtab中,并通过重定位段,确定了符号的重定位方法

    create_module通过syscall2宏来调用__NR_create_module

    #define _syscall2(type,name,type1,arg1,type2,arg2)\
    type name(type1 arg1,type2 arg2)\
    {\
    long __res;\
    __asm__ volatile("int $0x80"\
    :"=a"(__res)\
    :"0"(__NR_##name),"b"((long)(arg1)),"c"((long)(arg2)));\
    __syscall_return(type,__res);\
    }

    5.

    insmod的处理流程

    主要是ELF结构,以及符号处理等等。
    1.get_kernel_info函数负责取得kernel中先以注册的modules,放入module_stat中.
    2.set_ncv_prefix(NULL);判断symbolname中是否有前缀,象_smp之类,一般没有。
    3.检查是否已有同名的module。
    4.obj_load,将。o文件读入到struct obj_file结构f中。
    5.比较kernel版本和module的版本,在版本的判断中,不是想象中的那样简单,还有是否有checksum的逻辑关系。
    6.add_kernel_symbols替换.o中的symbol为ksyms中的符号值
    7.create_this_module(f, m_name)生成module结构,加入module_list中。
    8.obj_check_undefineds检查是否还有un_def的symbol
    9.add_archdata添加结构相关的section,不过i386没什么用。
    10.add_kallsyms如果symbol使用的都是kernel提供的,就添加一个.kallsyms section
    11.obj_load_size计算module的大小
    12.create_module调用sys_create_module系统调用创建模块,分配module的空间
    13.obj_relocate重定位module文件中.text中的地址
    14.init_module先在用户空间创建module结构的image影响,是由sys_init_module系统调用实现向kernel的copy。

    6.

    总结:

    对ELF文件格式还需要再加强学习一下,重点是重定位跟符号方面。多结合代码来学习,加强对系统的理解

    7.

    链接:
    http://www.nsfocus.net/index.php?act=magazine&do=view&mid=1487
    http://blog.csdn.net/sailor_8318/article/details/2954380
    http://blog.chinaunix.net/space.php?uid=1817735&do=blog&id=2837068
    Modultils工具源码分析
    http://blog.csdn.net/ganggexiongqi/article/details/6823960
    http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=315820&fpart=all
    http://blog.csdn.net/lihaoweiv/article/details/6601009

    http://wenku.baidu.com/view/8192da3f0912a216147929f2.html

  • 相关阅读:
    echarts官网上的动态加载数据bug被我解决。咳咳/。
    jquery中的jsonp和js中的jsonp还有配合php实现的jsonp。
    jquery中的done和always解决ajax问题
    vue2.0使用watch监听对象属性
    gulp配合vue压缩代码格式化
    支持flv的播放神器
    前端组件化-Web Components【转】
    自定义异步加载资源插件
    【leetcode刷题笔记】Two Sum
    【leetcode刷题笔记】Longest Common Prefix
  • 原文地址:https://www.cnblogs.com/moonflow/p/2305190.html
Copyright © 2020-2023  润新知