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

原文地址:https://www.cnblogs.com/moonflow/p/2305190.html