if (owner)//NULL,下边不需赋值
*owner = fsa.owner;
if (crc)//得到该符号的crc值
*crc = fsa.crc;
//返回以name命名的内核符号结构
return fsa.sym;
}
//没有找到内核符号,返回NULL
pr_debug('Failed to find symbol %sn', name);
return NULL;
}
bool each_symbol_section(bool (*fn)(const struct symsearch *arr,struct module *owner,void *data),void *data)
{
struct module *mod;
//第一部分在内核导出的符号表中查找对应的符号,找到就返回对应的符号信息,否则,再进行第二部分查找.
/*struct symsearch {
const struct kernel_symbol *start, *stop;
const unsigned long *crcs;
enum {
NOT_GPL_ONLY,
GPL_ONLY,
WILL_BE_GPL_ONLY,
} licence;
bool unused;
};
*/
static const struct symsearch arr[] = {
{ __start___ksymtab, __stop___ksymtab,
__start___kcrctab,NOT_GPL_ONLY, false },
{ __start___ksymtab_gpl, __stop___ksymtab_gpl,
__start___kcrctab_gpl,GPL_ONLY, false },
{ __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future,
__start___kcrctab_gpl_future,WILL_BE_GPL_ONLY, false },
#ifdef CONFIG_UNUSED_SYMBOLS
{ __start___ksymtab_unused, __stop___ksymtab_unused,
__start___kcrctab_unused,NOT_GPL_ONLY, true },
{ __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl,
__start___kcrctab_unused_gpl,GPL_ONLY, true },
#endif
};
if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data))
return true;
//在内核导出的符号表中没有找到对应的符号,则在系统已加载的模块中查找
list_for_each_entry_rcu(mod, &modules, list) {
struct symsearch arr[] = {
{ mod->syms, mod->syms + mod->num_syms,
mod->crcs,NOT_GPL_ONLY, false },
{ mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms,
mod->gpl_crcs,GPL_ONLY, false },
{ mod->gpl_future_syms,mod->gpl_future_syms + mod->num_gpl_future_syms,
mod->gpl_future_crcs,WILL_BE_GPL_ONLY, false },
#ifdef CONFIG_UNUSED_SYMBOLS
{ mod->unused_syms,mod->unused_syms + mod->num_unused_syms,
mod->unused_crcs, NOT_GPL_ONLY, true },
{ mod->unused_gpl_syms,mod->unused_gpl_syms + mod->num_unused_gpl_syms,
mod->unused_gpl_crcs,GPL_ONLY, true },
#endif
};
//若模块状态为MODULE_STATE_UNFORMED,则此模块的符号不可用
if (mod->state == MODULE_STATE_UNFORMED)
continue;
if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data))
return true;
}
return false;
}
static bool each_symbol_in_section(const struct symsearch *arr,unsigned int arrsize,struct module *owner,bool (*fn)(const struct symsearch *syms,struct module *owner,void *data),void *data)
{
unsigned int j;
//调用find_symbol_in_section()对每个数组指定的符号地址范围进行查找
for (j = 0; j < arrsize; j++) {
if (fn(&arr[j], owner, data))//调用find_symbol_in_section()
return true;
}
return false;
}
static bool find_symbol_in_section(const struct symsearch *syms,struct module *owner,void *data)
{
struct find_symbol_arg *fsa = data;
struct kernel_symbol *sym;
//在范围内查找符号名为fsa->name的内核符号
sym = bsearch(fsa->name, syms->start, syms->stop - syms->start,sizeof(struct kernel_symbol), cmp_name);
//若找到内核符号,则对其进行check是否有效
if (sym != NULL && check_symbol(syms, owner, sym - syms->start, data))
return true;
return false;
}
void *bsearch(const void *key, const void *base, size_t num, size_t size,int (*cmp)(const void *key, const void *elt))
{
size_t start = 0, end = num;
int result;
while (start < end) {//折半查找
size_t mid = start + (end - start) / 2;
//调用cmp_name()函数比较符号名是否一致
result = cmp(key, base + mid * size);
if (result < 0)
end = mid;
else if (result > 0)
start = mid + 1;
else
return (void *)base + mid * size;
}
return NULL;
}
static int cmp_name(const void *va, const void *vb)
{
const char *a;
const struct kernel_symbol *b;
a = va; b = vb;
return strcmp(a, b->name);
}
static bool check_symbol(const struct symsearch *syms,struct module *owner,unsigned int symnum, void *data)
{
struct find_symbol_arg *fsa = data;
if (!fsa->gplok) {//若未设置gplok,则必须为GPL许可
if (syms->licence == GPL_ONLY)
return false;
if (syms->licence == WILL_BE_GPL_ONLY && fsa->warn) {
printk(KERN_WARNING 'Symbol %s is being used '
'by a non-GPL module, which will not '
'be allowed in the futuren', fsa->name);
}
}
#ifdef CONFIG_UNUSED_SYMBOLS
if (syms->unused && fsa->warn) {
printk(KERN_WARNING 'Symbol %s is marked as UNUSED, '
'however this module is using it.n', fsa->name);
printk(KERN_WARNING'This symbol will go away in the future.n');
printk(KERN_WARNING
'Please evalute if this is the right api to use and if '
'it really is, submit a report the linux kernel '
'mailinglist together with submitting your code for '
'inclusion.n');
}
#endif
fsa->owner = owner;//符号所属模块
//#define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL)
fsa->crc = symversion(syms->crcs, symnum);//符号的crc值
fsa->sym = &syms->start[symnum];//返回的符号结构
return true;
}
static int check_version(Elf_Shdr *sechdrs,unsigned int versindex,const char *symname,struct module *mod, const unsigned long *crc,const struct module *crc_owner)
{
unsigned int i, num_versions;
struct modversion_info *versions;
//若系统中的该符号crc值为0,则直接返回1完事
if (!crc)
return 1;
//若该模块的elf格式文件中没有__versions节区,则尝试强制加载模块
//但是try_to_force_load()函数的实现依赖于CONFIG_MODULE_FORCE_LOAD宏是否定义。而该宏默认是没有定义的,所以这里会返回失败,看来内核并不推荐强制加载模块。
if (versindex == 0)
return try_to_force_load(mod, symname) == 0;
//找到模块“__versions”节区在内存映像中的起始地址。相当于节区的contents内容
versions = (void *) sechdrs[versindex].sh_addr;
num_versions = sechdrs[versindex].sh_size/ sizeof(struct modversion_info);
for (i = 0; i < num_versions; i++) {
if (strcmp(versions[i].name, symname) != 0)//在此节区中找到要比较的符号
continue;
//比较该模块中的符号crc值和现在系统上的内核符号的crc值是否一致
if (versions[i].crc == maybe_relocated(*crc, crc_owner))
return 1;
pr_debug('Found checksum %lX vs module %lXn',maybe_relocated(*crc, crc_owner), versions[i].crc);
goto bad_version;
}
//若在“__versions”节区没有找到要比较的符号,则会给出加载模块时常见错误:“no symbol version for”
printk(KERN_WARNING '%s: no symbol version for %sn',mod->name, symname);
return 0;
bad_version:
//如果比较符号的额crc值不一致,则会给出加载模块时常见错误“disagrees about version of symbol”
printk('%s: disagrees about version of symbol %sn',mod->name, symname);
return 0;
}
static int try_to_force_load(struct module *mod, const char *reason)
{
#ifdef CONFIG_MODULE_FORCE_LOAD
//若选项CONFIG_MODULE_FORCE_LOAD打开,则检查tainted_mask是否设置了TAINT_FORCED_MODULE标志,若没有给出警告信息
if (!test_taint(TAINT_FORCED_MODULE))
printk(KERN_WARNING '%s: %s: kernel tainted.n',mod->name, reason);
//设置mod->taints和tainted_mask的TAINT_FORCED_MODULE标志,表示强制加载该模块,并返回正确值0
add_taint_module(mod, TAINT_FORCED_MODULE, LOCKDEP_NOW_UNRELIABLE);
return 0;
#else
//若选项CONFIG_MODULE_FORCE_LOAD未打开,则直接返回错误
return -ENOEXEC;
#endif
}
static int check_modinfo(struct module *mod, struct load_info *info, int flags)
{
//从模块.modinfo节区中获得version magic
const char *modmagic = get_modinfo(info, 'vermagic');
int err;
if (flags & MODULE_INIT_IGNORE_VERMAGIC)
modmagic = NULL;
//若version magic为0,则尝试强制加载
if (!modmagic) {
err = try_to_force_load(mod, 'bad vermagic');
if (err)
return err;
}
//与内核的vermagic是否一致,若不一致,给出著名错误:“version magic ... should be ...”,返回错误码
else if (!same_magic(modmagic, vermagic, info->index.vers)) {
printk(KERN_ERR '%s: version magic '%s' should be '%s'n',mod->name, modmagic, vermagic);
return -ENOEXEC;
}
//返回.modinfo节区中intree=“...”的内容,若不存在设置标志TAINT_OOT_MODULE
if (!get_modinfo(info, 'intree'))
add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK);
//返回.modinfo节区中staging=“...”的内容,存在设置标志TAINT_CRAP
if (get_modinfo(info, 'staging')) {
add_taint_module(mod, TAINT_CRAP, LOCKDEP_STILL_OK);
printk(KERN_WARNING '%s: module is from the staging directory,'' the quality is unknown, you have been warned.n',mod->name);
}
//取出.modinfo节区的license字段指定的license类型,并对此license检查
set_license(mod, get_modinfo(info, 'license'));
return 0;
}
static char *get_modinfo(struct load_info *info, const char *tag)
{
char *p;
unsigned int taglen = strlen(tag);
//找到模块.modinfo节区
Elf_Shdr *infosec = &info->sechdrs[info->index.info];
unsigned long size = infosec->sh_size;
//查找.modinfo节区中的内容,返回'*tag'字符串=后边的内容
for (p = (char *)infosec->sh_addr; p; p = next_string(p, &size))
{
if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
return p + taglen + 1;
}
return NULL;
}
static inline int same_magic(const char *amagic, const char *bmagic, bool has_crcs)
{
//从字符串中依次取出以“ ”结尾的字符串段进行比较
if (has_crcs) {
amagic += strcspn(amagic, ' ');
bmagic += strcspn(bmagic, ' ');
}
return strcmp(amagic, bmagic) == 0;
}
static void set_license(struct module *mod, const char *license)
{
if (!license)
license = 'unspecified';
//比较模块的license类型是否是内核指定的GPL类型,若不是则设置TAINT_PROPRIETARY_MODULE标志
if (!license_is_gpl_compatible(license)) {
if (!test_taint(TAINT_PROPRIETARY_MODULE))
printk(KERN_WARNING '%s: module license '%s' taints kernel.n', mod->name, license);
add_taint_module(mod, TAINT_PROPRIETARY_MODULE,LOCKDEP_NOW_UNRELIABLE);
}
}
static inline int license_is_gpl_compatible(const char *license)
{
return (strcmp(license, 'GPL') == 0
|| strcmp(license, 'GPL v2') == 0
|| strcmp(license, 'GPL and additional rights') == 0
|| strcmp(license, 'Dual BSD/GPL') == 0
|| strcmp(license, 'Dual MIT/GPL') == 0
|| strcmp(license, 'Dual MPL/GPL') == 0);
}
static void layout_sections(struct module *mod, struct load_info *info)
{
//注意:节的复制是按照原来ELF的顺序,将所有标志包含SHF_ALLOC的节都复制到相应的分配空间(module_core/module_init),例外的情况是SHT_NOBITS,也就是BSS段,文件中没有分配空间,因此不需要复制.
//sections函数首先为标记了SHF_ALLOC的section定义了四种类型:code, read-only data,read-write data和small data. 带有SHF_ALLOC的section必定属于四类中的一类。函数遍历section header table中的所有项,将section name不是以'.init'开始的section划归为COREsection. 以'.init'开始的section划归为INIT section.
static unsigned long const masks[][2] = {
{ SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL },//code
{ SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL },//read only data
{ SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL },//read-write data
{ ARCH_SHF_SMALL | SHF_ALLOC, 0 }//small data
};
unsigned int m, i;
//遍历所有节区初始化sh_entsize成员
//某些节区中包含固定大小的项目,如符号表。对于这类节区,此成员sh_entsize给出每个表项的长度字节数。如果节区中并不包含固定长度表项的表格,此成员取值为 0。
for (i = 0; i < info->hdr->e_shnum; i++)
info->sechdrs[i].sh_entsize = ~0UL;
//划分为两部分: CORE INIT
//第1部分CORE:查找标志中含有SHF_ALLOC的section
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开头的节区划分到CORE部分
if ((s->sh_flags & masks[m][0]) != masks[m][0]
上一篇:Linux驱动之建立一个hello模块
下一篇:S3C2440看门狗定时器原理
推荐阅读最新更新时间:2024-11-16 17:21
设计资源 培训 开发板 精华推荐
- openPCR
- usb-2-ttl
- LT1171HVCQ、用于离线应用的 2.5A 驱动高压 FET 的典型应用
- 使用 STR91xFA 外部存储器接口 (EMI) 和 16 位 SRAM 多路复用总线连接的应用
- 使用 NXP Semiconductors 的 PCA9538 的参考设计
- 使用 ROHM Semiconductor 的 BD46395 的参考设计
- 使用 NXP Semiconductors 的 MPC860T 的参考设计
- 使用 MaxLinear, Inc 的 SP6656 的参考设计
- sx1308升降压模块(丝印B628)(sepic)
- AZ431A可调精密并联稳压器的典型应用
- Allegro MicroSystems 在 2024 年德国慕尼黑电子展上推出先进的磁性和电感式位置感测解决方案
- 左手车钥匙,右手活体检测雷达,UWB上车势在必行!
- 狂飙十年,国产CIS挤上牌桌
- 神盾短刀电池+雷神EM-i超级电混,吉利新能源甩出了两张“王炸”
- 浅谈功能安全之故障(fault),错误(error),失效(failure)
- 智能汽车2.0周期,这几大核心产业链迎来重大机会!
- 美日研发新型电池,宁德时代面临挑战?中国新能源电池产业如何应对?
- Rambus推出业界首款HBM 4控制器IP:背后有哪些技术细节?
- 村田推出高精度汽车用6轴惯性传感器
- 福特获得预充电报警专利 有助于节约成本和应对紧急情况