|| (s->sh_flags & masks[m][1])
|| s->sh_entsize != ~0UL || strstarts(sname, '.init'))
continue;
//把由于对齐产生的偏移保存到节区的sh_entsize字段,后边mod通过sh_entsize就可以找到该节区存储的位置。并把符合要求的节区大小加到mod->core_size
s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
}
//由于我们节的复制是按顺序的,而.text节是第一个节,因此mod->module_core实际上指向的就是.text段。而mod->core_text_size中也包含了.text节的大小.
switch (m) {
case 0: //可执行的段,代码段都一样
mod->core_size = debug_align(mod->core_size);
mod->core_text_size = mod->core_size;
break;
case 1: //只读段
mod->core_size = debug_align(mod->core_size);
mod->core_ro_size = mod->core_size;
break;
case 3: //所有段
mod->core_size = debug_align(mod->core_size);
break;
}
}
//第2部分INIT
for (m = 0; m < ARRAY_SIZE(masks); ++m) {
for (i = 0; i < info->hdr->e_shnum; ++i) {
Elf_Shdr *s = &info->sechdrs[i];
const char *sname = info->secstrings + s->sh_name;
//含有SHF_ALLOC的section需要加载到最终的内存
//含有SHF_ALLOC的section并且以init开头的划分到INIT部分
if ((s->sh_flags & masks[m][0]) != masks[m][0]
|| (s->sh_flags & masks[m][1])
|| s->sh_entsize != ~0UL
|| !strstarts(sname, '.init'))
continue;
//把由于对齐产生的偏移保存到节区的sh_entsize字段,并把符合要求的节区大小加到 mod->init_size
s->sh_entsize = (get_offset(mod, &mod->init_size, s, i) | INIT_OFFSET_MASK);
}
switch (m) {
case 0://代码段
mod->init_size = debug_align(mod->init_size);
mod->init_text_size = mod->init_size;
break;
case 1://只读段
mod->init_size = debug_align(mod->init_size);
mod->init_ro_size = mod->init_size;
break;
case 3://所有段
mod->init_size = debug_align(mod->init_size);
break;
}
}
}
static long get_offset(struct module *mod, unsigned int *size,Elf_Shdr *sechdr, unsigned int section)
{
//#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
//#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
//#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
long ret;
*size += arch_mod_section_prepend(mod, section);//空函数,返回0
ret = ALIGN(*size, sechdr->sh_addralign ?: 1);//返回*size字节对齐后的值
*size = ret + sechdr->sh_size;//把当前节区的size也加到*size上
return ret;
}
static void layout_symtab(struct module *mod, struct load_info *info)
{
Elf_Shdr *symsect = info->sechdrs + info->index.sym;//找到符号表节区头部
Elf_Shdr *strsect = info->sechdrs + info->index.str;//找到字符串表节区头部
const Elf_Sym *src;
unsigned int i, nsrc, ndst, strtab_size = 0;
//符号节区设置SHF_ALLOC标志,表示在内存占空间
symsect->sh_flags |= SHF_ALLOC;
//设置符号表节区每个表项的长度字节数,并把符号表的size加到mod->init_size
symsect->sh_entsize = get_offset(mod, &mod->init_size, symsect,info->index.sym) | INIT_OFFSET_MASK;
pr_debug('t%sn', info->secstrings + symsect->sh_name);
src = (void *)info->hdr + symsect->sh_offset;//符号表节区内容起始地址
nsrc = symsect->sh_size / sizeof(*src);//符号表项个数
//计算该模块包含的所有符号名长度所占的空间
for (ndst = i = 0; i < nsrc; i++) {
//is_core_symbol检查符号是否有效且属于SHF_ALLOC
if (i == 0 || is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
strtab_size += strlen(&info->strtab[src[i].st_name])+1;
ndst++;
}
}
//将mod->core_size按符号节区对齐约束对齐后得到的值返回给info->symoffs
info->symoffs = ALIGN(mod->core_size, symsect->sh_addralign ?: 1);
//将符号表节区内容(即ndst个符号表项)添加到core部分的后边,并重新设置core部分的size。
info->stroffs = mod->core_size = info->symoffs + ndst * sizeof(Elf_Sym);
//core部分再把符号名的字符串所占空间加上
mod->core_size += strtab_size;
//注意:这里只是为符号项和符号的名称长度分配了空间,还并没有将内容拷贝过来,在下边的add_kallsyms函数中进行拷贝!!!
//把字符串节区加到模块的init部分
strsect->sh_flags |= SHF_ALLOC;//字符串节区也设置上需要内存空间标志
strsect->sh_entsize = get_offset(mod, &mod->init_size, strsect,info->index.str) | INIT_OFFSET_MASK;
pr_debug('t%sn', info->secstrings + strsect->sh_name);
}
static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,unsigned int shnum)
{
const Elf_Shdr *sec;
//未定义的符号,或者符号索引号大于符号总个数,或者符号名为空则直接返回0
if (src->st_shndx == SHN_UNDEF|| src->st_shndx >= shnum|| !src->st_name)
return false;
//找到符号所在的节区,若该节区不占内存,则也返回0
sec = sechdrs + src->st_shndx;
if (!(sec->sh_flags & SHF_ALLOC)
#ifndef CONFIG_KALLSYMS_ALL
|| !(sec->sh_flags & SHF_EXECINSTR)
#endif
|| (sec->sh_entsize & INIT_OFFSET_MASK))
return false;
return true;
}
static int move_module(struct module *mod, struct load_info *info)
{
int i;
void *ptr;
//在内核空间为模块的core部分分配空间,返回地址
ptr = module_alloc_update_bounds(mod->core_size);
//但是要扫描这个指针所分配的内存的内容。分配数据结构那么该结构本身不打印,但是会扫描结构内部的成员变量,是否引用其他指针。
kmemleak_not_leak(ptr);
if (!ptr)
return -ENOMEM;
memset(ptr, 0, mod->core_size);
mod->module_core = ptr;//地址赋给module_core成员
//在内核空间为init section分配内存,初始化后存储在module对象的module_init成员中
if (mod->init_size) {
//为模块的init部分分配空间,返回地址
ptr = module_alloc_update_bounds(mod->init_size);
kmemleak_ignore(ptr);
if (!ptr) {
module_free(mod, mod->module_core);
return -ENOMEM;
}
memset(ptr, 0, mod->init_size);
mod->module_init = ptr;//地址赋给module_init
} else
mod->module_init = NULL;
/* Transfer each section which specifies SHF_ALLOC */
pr_debug('final section addresses:n');
//遍历所有节区,拷贝需要占用内存(标志为SHF_ALLOC的节区)的段到init section 或core section,并且调整各个节区的运行时地址
for (i = 0; i < info->hdr->e_shnum; i++) {
void *dest;
Elf_Shdr *shdr = &info->sechdrs[i];
if (!(shdr->sh_flags & SHF_ALLOC))
continue;
//如果节区首部的sh_entsize的最高位设置的话,表示该段属于init section,则从module_init开始的内存中获取当前段应该存储的地址,否则从module_core开始的内存中获取当前段应该存储的地址。
if (shdr->sh_entsize & INIT_OFFSET_MASK)
dest = mod->module_init + (shdr->sh_entsize & ~INIT_OFFSET_MASK);
else
dest = mod->module_core + shdr->sh_entsize;
//将当前节区的内容从ELF文件头拷贝到指定的段(init section或core section)中
if (shdr->sh_type != SHT_NOBITS)
memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
//更改节区的运行时地址,sh_addr原先存储的地址是相对于ELF文件头的地址,现在是在内核空间分配新的内存空间后,节区新的运行地址(即节区内容的起始地址)。
shdr->sh_addr = (unsigned long)dest;
pr_debug('t0x%lx %sn',(long)shdr->sh_addr, info->secstrings + shdr->sh_name);
}
return 0;
}
static void *module_alloc_update_bounds(unsigned long size)
{
//在内核空间中分配size大小的空间,返回起始地址
void *ret = module_alloc(size);
//更新模块边界值
if (ret) {
mutex_lock(&module_mutex);
if ((unsigned long)ret < module_addr_min)
module_addr_min = (unsigned long)ret;
if ((unsigned long)ret + size > module_addr_max)
module_addr_max = (unsigned long)ret + size;
mutex_unlock(&module_mutex);
}
return ret;
}
static int add_unformed_module(struct module *mod)
{
int err;
struct module *old;
mod->state = MODULE_STATE_UNFORMED;
again:
mutex_lock(&module_mutex);
//在内核中查找此模块是否已加入链表
old = find_module_all(mod->name, strlen(mod->name), true);
if (old != NULL) {
if (old->state == MODULE_STATE_COMING|| old->state == MODULE_STATE_UNFORMED) {
/* Wait in case it fails to load. */
mutex_unlock(&module_mutex);
err = wait_event_interruptible(module_wq,finished_loading(mod->name));
if (err)
goto out_unlocked;
goto again;
}
err = -EEXIST;
goto out;
}
//若还没有,则挂入全局模块链表modules
list_add_rcu(&mod->list, &modules);
err = 0;
out:
mutex_unlock(&module_mutex);
out_unlocked:
return err;
}
static int module_unload_init(struct module *mod)
{
//初始化多处理下用于引用计数的refptr成员
mod->refptr = alloc_percpu(struct module_ref);
if (!mod->refptr)
return -ENOMEM;
//初始化module对象的链表
INIT_LIST_HEAD(&mod->source_list);
INIT_LIST_HEAD(&mod->target_list);
//模块计数加1
__this_cpu_write(mod->refptr->incs, 1);
/* Backwards compatibility macros put refcount during init. */
mod->waiter = current;
return 0;
}
static void find_module_sections(struct module *mod, struct load_info *info)
{
mod->kp = section_objs(info, '__param',sizeof(*mod->kp), &mod->num_kp);
mod->syms = section_objs(info, '__ksymtab',sizeof(*mod->syms), &mod->num_syms);
mod->crcs = section_addr(info, '__kcrctab');
mod->gpl_syms = section_objs(info, '__ksymtab_gpl',sizeof(*mod->gpl_syms),&mod->num_gpl_syms);
mod->gpl_crcs = section_addr(info, '__kcrctab_gpl');
mod->gpl_future_syms = section_objs(info,'__ksymtab_gpl_future',
sizeof(*mod->gpl_future_syms),&mod->num_gpl_future_syms);
mod->gpl_future_crcs = section_addr(info, '__kcrctab_gpl_future');
#ifdef CONFIG_UNUSED_SYMBOLS
mod->unused_syms = section_objs(info, '__ksymtab_unused',sizeof(*mod->unused_syms),&mod->num_unused_syms);
mod->unused_crcs = section_addr(info, '__kcrctab_unused');
mod->unused_gpl_syms = section_objs(info, '__ksymtab_unused_gpl',sizeof(*mod->unused_gpl_syms),&mod->num_unused_gpl_syms);
mod->unused_gpl_crcs = section_addr(info, '__kcrctab_unused_gpl');
#endif
#ifdef CONFIG_CONSTRUCTORS
mod->ctors = section_objs(info, '.ctors',sizeof(*mod->ctors), &mod->num_ctors);
#endif
#ifdef CONFIG_TRACEPOINTS
mod->tracepoints_ptrs = section_objs(info, '__tracepoints_ptrs',sizeof(*mod->tracepoints_ptrs),&mod->num_tracepoints);
#endif
#ifdef HAVE_JUMP_LABEL
mod->jump_entries = section_objs(info, '__jump_table',sizeof(*mod->jump_entries),&mod->num_jump_entries);
#endif
#ifdef CONFIG_EVENT_TRACING
mod->trace_events = section_objs(info, '_ftrace_events',sizeof(*mod->trace_events),&mod->num_trace_events);
#endif
#ifdef CONFIG_TRACING
mod->trace_bprintk_fmt_start = section_objs(info, '__trace_printk_fmt',sizeof(*mod->trace_bprintk_fmt_start),&mod->num_trace_bprintk_fmt);
#endif
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
/* sechdrs[0].sh_size is always zero */
mod->ftrace_callsites = section_objs(info, '__mcount_loc',sizeof(*mod->ftrace_callsites),&mod->num_ftrace_callsites);
#endif
mod->extable = section_objs(info, '__ex_table',sizeof(*mod->extable), &mod->num_exentries);
if (section_addr(info, '__obsparm'))
printk(KERN_WARNING '%s: Ignoring obsolete parametersn',mod->name);
info->debug = section_objs(info, '__verbose',sizeof(*info->debug), &info->num_debug);
}
static void *section_objs(const struct load_info *info,const char *name,size_t object_size,unsigned int *num)
{
//根据节区名找到节区索引值
unsigned int sec = find_sec(info, name);
//计算节区项目数
*num = info->sechdrs[sec].sh_size / object_size;
//返回节区起始地址
return (void *)info->sechdrs[sec].sh_addr;
}
static int check_module_license_and_versions(struct module *mod)
{
if (strcmp(mod->name, 'ndiswrapper') == 0)
add_taint(TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE);
上一篇:Linux驱动之建立一个hello模块
下一篇:S3C2440看门狗定时器原理
推荐阅读最新更新时间:2024-11-16 17:23
设计资源 培训 开发板 精华推荐
- VNCLO-MB1A,基于Vinculum II的Vinco主板开发模块,VNC2双USB Host/Slave IC
- LTC3855 的典型应用 - 具有差分远程检测的双通道、多相同步 DC/DC 控制器
- 【牡丹江师范学院】乌龟的贴心小管家
- MIKROE-2814,基于 BQ25570 太阳能收集器的太阳能 CLICK 板
- LTC3110IUF 3.3V/2A 输出的典型应用电路来自具有有源电压平衡的超级电容器备份/再充电应用
- 基于STM32的武警哨位联动报警系统设计,支持以太网和WIFI通信(硬件、源码、论文等)
- AM1D-0505D-RZ ±5V 1 瓦 DC/DC 转换器的典型应用
- 具有集成 MOSFET 的 ER2120QI 2A、4.5 至 5.5 Vin 同步降压稳压器的典型应用电路
- LTC2415CGN 模数转换器的典型应用电路
- 使用 Infineon Technologies AG 的 TDA 16846G 的参考设计
- Allegro MicroSystems 在 2024 年德国慕尼黑电子展上推出先进的磁性和电感式位置感测解决方案
- 左手车钥匙,右手活体检测雷达,UWB上车势在必行!
- 狂飙十年,国产CIS挤上牌桌
- 神盾短刀电池+雷神EM-i超级电混,吉利新能源甩出了两张“王炸”
- 浅谈功能安全之故障(fault),错误(error),失效(failure)
- 智能汽车2.0周期,这几大核心产业链迎来重大机会!
- 美日研发新型电池,宁德时代面临挑战?中国新能源电池产业如何应对?
- Rambus推出业界首款HBM 4控制器IP:背后有哪些技术细节?
- 村田推出高精度汽车用6轴惯性传感器
- 福特获得预充电报警专利 有助于节约成本和应对紧急情况