Linux驱动之内核加载模块过程分析

发布者:upsilon30最新更新时间:2024-08-20 来源: cnblogs关键字:Linux驱动  过程分析 手机看文章 扫描二维码
随时随地手机看文章

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]

[1] [2] [3] [4] [5] [6]
关键字:Linux驱动  过程分析 引用地址:Linux驱动之内核加载模块过程分析

上一篇:Linux驱动之建立一个hello模块
下一篇:S3C2440看门狗定时器原理

推荐阅读最新更新时间:2024-11-16 17:21

linux i2c 设备测试,i2c-dev驱动测试代码
embed-linux version:linux-2.6.39-exp vmware-linux:ubuntu14.04 hardware: core chip at91sam9x25 cross-compile:arm-none-linux-gnueabi-gcc 4.5.2 code: #include stdio.h #include stdlib.h #include unistd.h #include sys/ioctl.h #include sys/types.h #include sys/stat.h #include fcntl.h //#include sys/select.h #include sys/tim
[单片机]
Linux驱动之一个简单的输入子系统程序编写
在Linux驱动之输入子系统简析已经分析过了输入子系统的构成,它是由设备层、核心层、事件层共同组成的。其中核心层提供一些设备层与事件层公用的函数,比如说注册函数、反注册函数、事件到来的处理函数等等;事件层其实在Linux内核里面已经帮我们写好了很多有关的事件;而设备层就跟我们新添加到输入系统的具体设备相关了。这里以JZ2440开发板上的4个按键作为输入子系统的按键:它定义的功能分别为:KEY_L、KEY_S、KEY_ENTER、KEY_LEFTSHIFT。这几个值是在includelinuxinput.h中被定义的。接下来就是编写程序: 直接贴出源程序: #include linux/module.h #include
[单片机]
<font color='red'>Linux</font><font color='red'>驱动</font>之一个简单的输入子系统程序编写
Linux驱动入门
阅读此文的方法: 阅读以下2个文件: hello.c,asdf.c。 此文假设读者: 已经能用C语言编写Linux应用程序, 理解"字符设备文件, 块设备文件, 主设备号, 次设备号", 会写简单的Shell脚本和Makefile。 1. "hello.c" -------------------------------- /* * 这是我们的第一个源文件, * 它是一个可以加载的内核模块, * 加载时显示"Hello,World!", * 卸载时显示"Bye!"。 * 需要说明一点,写内核或内核模块不能用写应用程序时的系统调用或函数库, * 因为我们写的就是为应用程序提供系统调用的代码。 * 内核有专用的函数库,如 linux/
[嵌入式]
XScale PXA270在Linux下的FPGA设备驱动
引言   Intel公司推出的XScale采用ARM V5TE结构,是Strong ARM的升级换代产品。XScale PXA270处理器最高主频可达624 MHz,加入了Wireless MMX、Intel SpeedStep等新技术,以其高性能、低功耗、多功能等特点在信息家电、工业控制等领域得到了广泛的应用。在嵌入式控制中,“微处理器+FPGA”是一种常用的解决方案。FPGA(现场可编程门阵列)有编程方便、集成度高、速度快等特点,电子设计人员可以通过硬件编程的方法来实现FPGA芯片各种功能的开发。在我们的一个数控平台的研究项目中,采用XScale PXA270作为主CPU,并对其进行FPGA扩展,使其具有插补、电机驱动、信
[嵌入式]
基于ARM9+Linux的DS18B20驱动程序设计
引言 随着嵌入式技术的发展,基于ARM和Linux的嵌入式产品越来越多, DS18B20 温度采集传感器在工业和生活上应用广泛,研究开发基于ARM9和Linux的DS18B20的驱动程序可以满足大部分温度采集平台的应用。 1 Linux设备驱动的开发过程 Linux操作系统通过各种驱动程序来操作硬件设备,它屏蔽了各种设备,设备驱动程序是操作系统内核和硬件之间的接口。从应用程序来看,硬件只是一个设备文件,应用程序可以像操作普通文件一样操作硬件设备。 1.1 设备的分类 Linux看待设备可区分为3种基本设备类型,分别为字符设备、块设备和网络设备: ①字符设备:字符设备是一种可以当作一个字节流来存取的设备,相当于一个文件,字符设备驱动
[电源管理]
基于ARM9+<font color='red'>Linux</font>的DS18B20<font color='red'>驱动</font>程序设计
基于内核对象的Linux输入子系统触摸屏的驱动设计
随着人们对操控要求的不断提高,电容触摸屏因为能支持多点触摸而得到广泛使用。本文基于Nokia和Intel公司合作开发的开源操作系统MeeGo,采用基于内核对象的Linux输入子系统来设计触摸屏的驱动。该方案极大地方便了触摸屏的驱动开发,可应用在车载娱乐、上网本、智能手机等电子产品上。 随着人们对操控要求的不断提高,市场上出现了越来越多的高端手机、平板电脑,这些产品共同的特点就是给人们提供了非常便利的操控方式,尤其是电容触摸屏的使用,它能很好地实现多点触控功能。多点触控技术是当今炙手可热的技术,它让人们的生活方式得到了前所未有的改变。电容触摸屏已经成为高端手机的标配,如苹果的iPhone以及HTC Motorola的一些高端
[单片机]
基于<font color='red'>内核</font>对象的<font color='red'>Linux</font>输入子系统触摸屏的<font color='red'>驱动</font>设计
利用频域时钟抖动分析加快设计验证过程
   简介   随着数据速率的提高,时钟抖动分析的需求也在与日俱增。在高速串行数据链路中,时钟抖动会影响发射机、传输线和接收机的数据抖动。保证时钟质量的测量也在不断发展。目前的重点是针对比特误码率,在时钟性能和系统性能之间建立直接联系。我们将探讨参考时钟的作用和时钟抖动对数据抖动的影响,并讨论在E5052B信号源分析仪(SSA)上运行的Agilent E5001A精确时钟抖动分析应用软件所配备的全新测量技术。该应用软件提供了前所未有的强大能力,可以对随机抖动(RJ)和周期抖动(PJ)分量超低 RJ测量和实时抖动频谱分析,使您能够提高设计质量。我们还将对新解决方案的实时测量功能进行讨论,这一功能能够加快设计验证过程。    参考
[工业控制]
数字电源市场发展过程中存在的问题分析
  随着节能成为当今世界的重大课题,电源 管理 也越来越受重视,技术水平的提升正不断优化系统的能效。但是,在“高集成度”、“数字化”等概念面前,企业更关注的还是市场和成本等现实问题。      电源管理芯片曾被形容为半导体行业的灰姑娘,这表明电源管理芯片长期以来被认为是半导体业界一个不起眼的配角。然而,与数字电路市场的大起大落不同,电源管理芯片保持着长期、稳定的增长势头。Databeans公司的 市场调查 结果显示,2007年电源管理芯片全球销售额超过80亿美元,预计今年的增长可达7%。随着原油价格的不断上升,节能领域的应用无疑是给灰姑娘穿上了红舞鞋,电源管理应用正迈步走向半导体产业的前台。      技术创新促进能效提
[电源管理]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • ARM裸机篇--按键中断
    先看看GPOI的输入实验:按键电路图:GPF1管教的功能:EINT1要使用GPF1作为EINT1的功能时,只要将GPFCON的3:2位配置成10就可以了!GPF1先配 ...
  • 网上下的--ARM入门笔记
    简单的介绍打今天起菜鸟的ARM笔记算是开张了,也算给我的这些笔记找个存的地方。为什么要发布出来?也许是大家感兴趣的,其实这些笔记之所 ...
  • 学习ARM开发(23)
    三个任务准备与运行结果下来看看创建任务和任运的栈空间怎么样的,以及运行输出。Made in china by UCSDN(caijunsheng)Lichee 1 0 0 ...
  • 学习ARM开发(22)
    关闭中断与打开中断中断是一种高效的对话机制,但有时并不想程序运行的过程中中断运行,比如正在打印东西,但程序突然中断了,又让另外一个 ...
  • 学习ARM开发(21)
    先要声明任务指针,因为后面需要使用。 任务指针 volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • 学习ARM开发(20)
  • 学习ARM开发(19)
  • 学习ARM开发(14)
  • 学习ARM开发(15)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved