AUTHOR: Joseph Yang (杨红刚) <[email protected]>
CONTENT: sys_init_module & sys_delete_module 简要分析
NOTE: linux-3.0
LAST MODIFIED:09-26-2011
-----------------------------------------------------------------------------------------------------------
Distributed and Embedded System Lab (分布式嵌入式系统实验室,兰州大学)
===============================================================
ref:ELF http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
内核模块的构建过程: http://blog.csdn.net/unbutun/article/details/6445983
- readelf -S module_name.ko //读ELF文件的各个段的信息
- ELF文件格式
模块加载过程:http://www.linuxforum.net/forum/showflat.php?Cat=&Board=security&Number=529127&page=0&view=collapsed&sb=5&o=31&fpart=
-------------------------------------------------------------------------------------------------
1. SYSCALL_DEFINE3的解释
ref: include/linux/syscalls.h & arch/x86/include/asm/linkage.h
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#ifdef CONFIG_FTRACE_SYSCALLS //我们不分析这一部分
//...
#else
#define SYSCALL_DEFINEx(x, sname, ...) \
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#endif
#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
//...
#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */
#define SYSCALL_DEFINE(name) asmlinkage long sys_##name
#define __SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */
#define __SC_DECL1(t1, a1) t1 a1
#define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
#define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
// arch/x86/include/asm/linkage.h
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
// __attribute__是关键字,是gcc的C语言扩展,regparm(0)表示不从寄存器传递参数
------------------------------------------------------------------------------------------------------------------------
2.
/* <1> sys_init_module */
/* This is where the real work happens */
SYSCALL_DEFINE3(init_module, void __user *, umod,
unsigned long, len, const char __user *, uargs);
SYSCALL_DEFINEx(3, _init_module, __VA_ARGS__)
//__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
__SYSCALL_DEFINEx(3, _init_module, __VA_ARGS__)
apply_relocate
//asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
asmlinkage long sys_init_module(__SC_DECL3(__VA_ARGS__))
//#define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
asmlinkage long sys_init_module(void __user * umod, __SC_DECL2(__VA_ARGS__))
//#define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
asmlinkage long sys_init_module(void __user * umod, unsigned long len, __SC_DECL1(__VA_ARGS__))
//#define __SC_DECL1(t1, a1) t1 a1
asmlinkage long sys_init_module(void __user * umod, unsigned long len, const char __user * uargs)
通过上面的步骤,可以得到函数原型:
asmlinkage long sys_init_module(void __user * umod, unsigned long len,
const char __user * uargs);
参数:
umod - 模块的名字
len -
uargs -
功能:
流程:
确保有插入和删除模块不受限制的权利,并且模块没有被禁止插入或删除??
//capable,modules_disabled
分配,加载模块,并创建相关的sysfs文件 //load_module
通知内核通知链module_notify_list上的监听者,模块状态变为MODULE_STATE_COMING
//blocking_notifier_call_chain
对于core和init设置相关的region设置 RO和 NX属性 // set_section_ro_nx
调用本模块的所有构造器 //do_mod_ctors
调用模块的init方法 //do_one_initcall
mod->state = MODULE_STATE_LIVE
唤醒module_wq 队列上等待本模块初始化的所有 任务
通知内核通知链module_notify_list上的监听者,模块状态变为MODULE_STATE_LIVE
//blocking_notifier_call_chain
等待所有的异步函数调用完成 //async_synchronize_full
获得module_mutex 锁// module_mutex作用之一就是保护全局的模块链表
//trim_init_extable
对init相关的region 取消 RO和NX属性的设置 //unset_module_init_ro_nx
释放与模块初始化相关的节区所占的内存 (mod->module_init )//module_free
2.1
函数:
/* Allocate and load the module: note that size of section 0 is always
zero, and we rely on this for optional sections. */
static struct module *load_module(void __user *umod,
unsigned long len,
const char __user *uargs);
参数:
umod -
len -
uargs -
功能:分配,加载模块,并创建相关的sysfs文件
流程:
对模块进行有效性检查,为模块分配内核空间,并复制内核到该空间,
并设置info->hdr和info->len //copy_and_check
布置模块,并分配相关的内存,把相关节区复制到最终镜像中 //layout_and_allocate
Init the unload section of the module. //module_unload_init
寻找可选节区,并设置 mod 中跟相关节区相关的项//find optional sections
//check_module_license_and_versions
Set up MODINFO_ATTR fields //setup_modinfo
Fix up syms, so that st_value is a pointer to location. //simplify_symbols
对info代表的模块做重定位操作 //apply_relocations, post_relocation
-------- //以下没有详细看源码,仅仅参考了源码中部分注释
Flush the instruction cache // flush_module_icache
把可选参数从用户空间复制到内核空间 //strndup_user
把模块的状态标记为MODULE_STATE_COMING
获得module_mutex 锁// module_mutex作用之一就是保护全局的模块链表
确定没有已经加载的同名模块//find_module
//dynamic_debug_setup
确定没有重复的导出符号//verify_export_symbols
//module_bug_finalize
把本模块添加到全局模块链表中//list_add_rcu
释放module_mutex锁 //mutex_unlock
//parse_args
在sysfs中创建与本模块相关的文件支持 //mod_sysfs_setup
释放临时模块镜像和其他临时辅助内存区//kfree, free_copy
//trace_module_load
2.2
函数:
/* Sets info->hdr and info->len. */
static int copy_and_check(struct load_info *info,
const void __user *umod, unsigned long len,
const char __user *uargs);
参数:
info -
umod -
len -
uargs -
功能:对模块进行有效性检查,为模块分配内核空间,并复制内核到该空间,
并设置info->hdr和info->len
流程:
如果模块的大小没有超过64M,则为模块分配 len大小的空间 //vmalloc
将整个模块ELF文件拷贝到内核空间 //copy_from_user
检查目标文件是否是 ELF 目标文件,是否是ELF文件的类型是可重定位文件,
体系结构,节区头部表格的表项大小是否正常//memcmp,elf_check_arch
检查所有 节区头部表项和文件头部所占空间不超过 len.
info->hdr = hdr;
info->len = len;
2.3
函数: static struct module *layout_and_allocate(struct load_info *info)
参数:
功能: 布置模块,并分配相关的内存,把相关节区复制到最终镜像中
//Figure out module layout, and allocate all the memory.
流程:
Set up our basic convenience variables and do some basic section verification //setup_load_info
检查modinfo相关的参数 //check_modinfo
对于x86构架此函数直接返回0//module_frob_arch_sections
如果“.data..percpu“节区的大小不为零,
则为之分配per cpu 域//percpu_modalloc
并清除该段sh_flags中的SHF_ALLOC标志// ????为什么这么做
更新mod中和core和init有关的size的值,并更新每个节区的sh_entsize//layout_sections
为symbol section 以及跟它相关的string table布置位置,更新相关size//layout_symtab
为mod指向的临时镜像中标记了SHF_ALLOC段分配内存,
并从临时镜像复制到最终的位置 //move_module
mod更新为最终模块的第一个节区的首地址
2.4
函数:
/*
* Set up our basic convenience variables (pointers to section headers,
* search for module section index etc), and do some basic section
* verification.
* Return the temporary module pointer (we'll replace it with the final
* one when we move the module sections around).
*/
static struct module *setup_load_info(struct load_info *info);
参数:
功能:Set up our basic convenience variables and do some basic section verification.
流程:
info->sechdrs指向节区头部,info->secstrings指向节区名称字符串
用在临时内存中的实际地址更新每个节区的sh_addr,
设置sh_flags标志使某些节区不在最终的内存镜像中出现。//rewrite_section_headers
查找符号表所在节区,
把它的索引存入info->index.sym
把它的节区头部表索引链的索引存入info->index.str
把它的名字字符串索引存入info->strtab
找到 “.gnu.linkonce.this_module“ 节区 //find_sec
把它的索引保存到info->index.mod
让mod指向该节区的首地址 //???????
找到.data..percpu节区//find_pcpusec
把它的索引保存到info->index.pcpu中
模块相关的检查工作//check_modstruct_version
返回临时的模块的指针
2.5 *
函数:static int rewrite_section_headers(struct load_info *info)
参数:info - 描述了加载到内存中的模块的实际参数
功能:用在临时内存中的实际地址更新每个节区的sh_addr,
设置sh_flags标志使某些节区不在最终的内存镜像中出现。
流程:
第0个节区的sh_addr置零
对除第0个节区外的所有节区执行如下操作:
检查确保所有非SHT_NOBITS类型节区没有被截断
把节区的sh_addr写为镜像在临时内存的实际地址
如果内核配置为不可卸载模块(CONFIG_MODULE_UNLOAD)
则,清除所有名称以'.exit'开始的节区的sh_flags中的SHF_ALLOC位,
表示该节区不加载到模块的内存镜像中
把版本节和info节不加载到最终的内核镜像中 //find_sec
2.6 *
函数:
/* Find a module section: 0 means not found. */
static unsigned int find_sec(const struct load_info *info, const char *name);
参数:
功能:返回info代表的镜像中名字为 name的节区的索引
流程:
2.7
函数:
static inline int check_modstruct_version(Elf_Shdr *sechdrs,
unsigned int versindex,
struct module *mod);
参数:
sechdrs - 指向ELF 文件头
versindex - 版本信息所在的节区的索引
mod - 指向.gnu.linkonce.this_module节区的首地址 ???
功能:总之,该函数做了模块相关的检查工作。
流程:
找到以“module_layout“开头的节区 //find_symbol
版本相关的信息的检查 //check_version
2.8
函数:
/* Find a symbol and return it, along with, (optional) crc and
* (optional) module which owns it. Needs preempt disabled or module_mutex. */
const struct kernel_symbol *find_symbol(const char *name,
struct module **owner,
const unsigned long **crc,
bool gplok,
bool warn);
参数:
功能:
Find a symbol and return it, along with, (optional) crc and (optional) module which owns it.
流程:
2.9
函数:static int check_modinfo(struct module *mod, struct load_info *info)
参数:
功能:检查modinfo相关的参数
流程:
返回模块信息所在节区中与“vermagic” 相关的字符串的地址 //get_modinfo
//至于tag “vermagic”对应的内容,你可以使用modinfo your_module 查看
检查模块的vermagic相关的内容是合理的 //same_magic
返回模块信息所在节区中与“staging” 相关的字符串的地址//get_modinfo
如果模块是来自staging文件夹下,
则是不成熟的模块,标记模块为(污染的)TAINT_CRAP
给出警告
gpl license相关的检查和设置 //get_modinfo,set_license
2.10
函数:static char *get_modinfo(struct load_info *info, const char *tag)
参数:
功能:返回模块信息所在节区中与tag 相关的字符串的地址
流程:
得到模块相关的信息所在的节区的指针
从中找到与tag 相关的字符串的地址
2.11
函数:static void set_license(struct module *mod, const char *license)
参数:
功能:gpl license相关的检查和设置
流程:
license必须是 "GPL","GPL v2","GPL and additional rights", "Dual BSD/GPL"
"Dual MIT/GPL", "Dual MPL/GPL"之一
否则,认为模块的license和gpl不兼容。
并把模块标记为TAINT_PROPRIETARY_MODULE
//license_is_gpl_compatible ,add_taint_module
2.12
函数:
static int percpu_modalloc(struct module *mod,
unsigned long size, unsigned long align)
参数:
功能:分配per cpu 域
流程:
分配per cpu 域 //__alloc_reserved_percpu
2.13
函数:
/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
might -- code, read-only data, read-write data, small data. Tally
sizes, and place the offsets into sh_entsize fields: high bit means it
belongs in init. */
static void layout_sections(struct module *mod, struct load_info *info);
参数:
功能:// Determine total sizes, and put offsets in sh_entsize.
//更新mod中和core和init有关的size的值,并更新每个节区的sh_entsize
更新mod的 mod->core_text_size
mod->core_size
mod->init_text_size
mod->init_ro_size //
mod->init_size //所有AX,A,AW,且以“.init"开头的节区大小之和
每个节区的sh_entsize(最高位为1表示属于“init”)
注意:
- ARCH_SHF_SMALL 在x86上为 0
- $ readelf -S name_of_your_module
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
流程:
所有节区的sh_entsize字段写为全1
遍历所有节区:
对于AX(SHF_EXECINSTR | SHF_ALLOC)类型的节区,A(SHF_ALLOC)类型的节区,
AW (SHF_WRITE | SHF_ALLOC)类型的节区,并且名字不是以".init"开头的节区:
把节区对齐后的大小累加到模块的core_size上, //get_offset
把由于对齐产生的偏移保存到节区的sh_entsize字段
将AX类型节区的大小保存到mod->core_text_size中 ,
将A(SHF_ALLOC)类型的节区(text and ro-data) 的大小与AX类型节区的大小之和
保存到 mod->core_ro_size
mod->core_size中保存有所有AX,A,AW,且不是以“.init"开头的所有节区大小之和
遍历所有节区:
对于AX(SHF_EXECINSTR | SHF_ALLOC)类型的节区,A(SHF_ALLOC)类型的节区,
AW (SHF_WRITE | SHF_ALLOC)类型的节区,并且名字是以".init"开头的节区:
把由于对齐产生的偏移保存到节区的sh_entsize字段 ,并把最高位值1,表示属于“init”
将AX类型节区的大小保存到mod->init_text_size中 ,
将A(SHF_ALLOC)类型的节区(text and ro-data) 的大小与AX类型节区的大小之和
保存到mod->init_ro_size
mod->init_size中保存有所有AX,A,AW,且以“.init"开头的节区大小之和
2.14 s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
函数:
/* Update size with this section: return offset. */
static long get_offset(struct module *mod, unsigned int *size,
Elf_Shdr *sechdr, unsigned int section)
参数:
功能:把sechdr指向的节区对齐后的大小加到size上,并返回由于对齐产生的偏移
流程:
//#define ALIGN(x, a) (((x) + ((typeof(x))(a) - 1)) & ~ ((typeof(x))(a) - 1))
2.15
函数:static void layout_symtab(struct module *mod, struct load_info *info)
参数:
功能:为symbol section 以及跟它相关的string table布置位置,更新相关size ????
流程:
/* Put symbol section at end of init part of module. */
为sym节区设置 SHF_ALLOC标志//sh_flags
把sym节区对齐后的大小加到mod->init_size,并返回对齐产生的偏移,//get_offset ---------- size
将偏移保存到sym节区的sh_entsize,并把最高位置1表示属于init
对于符号表的每个符号表项, 执行如下动作:
如果该符号表项属于 core symbol,则
把字符串映射到info->strmap中//如果字符非空,则置1,否则为0
//注意:符号表中的属于core symbol的字符串按照在
//sym 节区中的顺序连续地映射到strmap中
/* Append room for core symbols at end of core part. */
mod->core_size更新为加上sym 节区后并对齐的值
/* Put string table section at end of init part of module. */
为sym节区关联的string table 节区设置SHF_ALLOC//sh_flags
把string table节区对齐后的大小加到mod->init_size,并返回对齐产生的偏移,//get_offset ---------- size
将偏移保存到string table节区的sh_entsize,并把最高位置1表示属于init
/* Append room for core symbols' strings at end of core part. */
更新 mod->core_size,增加存储symbols' strings 中属于core symbol的字符串的大小的值
2.16
函数:
static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
unsigned int shnum)
参数:
功能:如果src 属于sechdrs代表的模块的core symbol,则返回true,否则,返回false
注意:INIT_OFFSET_MASK:最高位为1表示属于init
2.17
函数:static int move_module(struct module *mod, struct load_info *info)
参数:
功能:为mod指向的临时镜像中标记了SHF_ALLOC段分配内存,
并从临时镜像复制到最终的位置
流程:
allocate virtually contiguous memory (mod->core_size) //module_alloc_update_bounds
mark an allocated object as false positive //kmemleak_not_leak
把分配的内存块清零 //memset
allocate virtually contiguous memory (mod->init_size) //module_alloc_update_bounds
ignore an allocated object //kmemleak_ignore
把分配的内存块清零 //memset
Transfer each section which specifies SHF_ALLOC // memcpy
2.18
函数:static void *module_alloc_update_bounds(unsigned long size)
参数:
功能: allocate virtually contiguous memory
流程:
allocate virtually contiguous memory // module_alloc
2.19
函数:static int apply_relocations(struct module *mod, const struct load_info *info)
参数:
功能:对info代表的模块做重定位操作
流程:
对于模块中所有SHT_REL类型节区:
//apply_relocate
对于模块中所有SHT_RELA类型节区:
//apply_relocate_add
3.
函数:
SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
unsigned int, flags)
即,
asmlinkage long sys_delete_module(const char __user * name_user, unsigned int flags);
参数:
功能:
流程:
确保有插入和删除模块不受限制的权利,并且模块没有被禁止插入或删除
//capable,modules_disabled
获得从用户空间传递到内核空间的模块名字 //strncpy_from_user
获得module_mutex锁 //mutex_lock_interruptible
得到要卸载的模块的指针 // find_module
检查,确认没有其他模块依赖要卸载的模块 // list_empty
检查模块的状态是否是 MODULE_STATE_LIVE
设置等待本模块退出 的进程为 current //mod->waiter = current
/* Stop the machine so refcounts can't move and disable module. */ //try_stop_module
等待模块的引用计数变为0 //wait_for_zero_refcount
释放module_mutex锁 //mutex_unlock
调用模块本身的exit函数 //mod->exit()
告诉通知链module_notify_list上的监听者,模块状态 变为 MODULE_STATE_GOING
//blocking_notifier_call_chain
等待所有的异步函数调用完成//async_synchronize_full
/* Store the name of the last unloaded module for diagnostic purposes */ //strlcpy
//free_module
3.1
函数:
/* Free a module, remove from lists, etc. */
static void free_module(struct module *mod)
参数:
功能:
流程:
删除相关的 sysfs中的文件 // mod_sysfs_teardown
/* Remove dynamic debug info */ // ddebug_remove_module
/* Arch-specific cleanup. */ // module_arch_cleanup
/* Module unload stuff */
/* Free any allocated parameters. */
对init相关的region 取消 RO和NX属性的设置 //unset_module_init_ro_nx
释放与模块初始化相关的节区所占的内存 (mod->module_init )//module_free
kfree(mod->args)
释放相关的percpu 变量 //percpu_modfree
/* Free lock-classes: */
/* Finally, free the core (containing the module structure) */
对core相关的region 取消 RO和NX属性的设置// unset_module_core_ro_nx(mod);
释放与core相关的节区所占的内存 //module_free(mod, mod->module_core);
//update_protections
-------------------- 相关的数据结构 ----------
#define ELFMAG "\177ELF" // include/linux/elf.h
/* Section header. */
typedef struct elf32_shdr {
Elf32_Word sh_name; /* Section name, index in string tbl */
Elf32_Word sh_type; /* Type of section */
Elf32_Word sh_flags; /* Miscellaneous section attributes */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Size of section in bytes */
Elf32_Word sh_link; /* Index of another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */ 包存了节区对齐产生的偏移量(layout_sections)
} Elf32_Shdr;
#define Elf_Shdr Elf32_Shdr
// include/linux/elf.h
/* The ELF file header. This appears at the start of every ELF file. */
typedef struct elf32_hdr{ //和用户空间的一样
unsigned char e_ident[EI_NIDENT]; //必须为 "\177ELF"/* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version;/* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
#define Elf_Ehdr Elf32_Ehdr
struct load_info {
Elf_Ehdr *hdr; // Pointer to ELF file header
unsigned long len; //整个ELF文件的大小
Elf_Shdr *sechdrs; // Pointer to Section header.
char *secstrings, *strtab; //secstrings: 节区名称字符串,
// strtab:和符号表所关联的字符串表的首地址
unsigned long *strmap; //和符号表相关联的字符串表节区的bit 映射 //layout_and_allocate(zalloc)
unsigned long symoffs, stroffs;//symoffs:mod->core_size对齐后的值
struct _ddebug *debug;
unsigned int num_debug;
struct {
unsigned int sym, str, mod, vers, info, pcpu;
// sym代表符号表节区索引,版本节,模块信息节,在镜像中的索引
//str代表和符号表相关联的字符串表的节区的头部索引
//mod指向.gnu.linkonce.this_module节的索引//该节中有模块的初始化和卸载方法
//pcpu指向 .data..percpu节区的索引
} index;
};
struct module
{
enum module_state state;
/* Member of list of modules */
struct list_head list;
/* Unique handle for this module */
char name[MODULE_NAME_LEN];
/* Sysfs stuff. */
struct module_kobject mkobj;
struct module_attribute *modinfo_attrs;
const char *version;
const char *srcversion;
struct kobject *holders_dir;
/* Exported symbols */
const struct kernel_symbol *syms;
const unsigned long *crcs;
unsigned int num_syms;
/* Kernel parameters. */
struct kernel_param *kp;
unsigned int num_kp;
/* GPL-only exported symbols. */
unsigned int num_gpl_syms;
const struct kernel_symbol *gpl_syms;
const unsigned long *gpl_crcs;
#ifdef CONFIG_UNUSED_SYMBOLS
/* unused exported symbols. */
const struct kernel_symbol *unused_syms;
const unsigned long *unused_crcs;
unsigned int num_unused_syms;
/* GPL-only, unused exported symbols. */
unsigned int num_unused_gpl_syms;
const struct kernel_symbol *unused_gpl_syms;
const unsigned long *unused_gpl_crcs;
#endif
/* symbols that will be GPL-only in the near future. */
const struct kernel_symbol *gpl_future_syms;
const unsigned long *gpl_future_crcs;
unsigned int num_gpl_future_syms;
/* Exception table */
unsigned int num_exentries;
struct exception_table_entry *extable;
/* Startup function. */
int (*init)(void);
/* If this is non-NULL, vfree after init() returns */
void *module_init;
/* Here is the actual code + data, vfree'd on unload. */
void *module_core; //
/* Here are the sizes of the init and core sections */
unsigned int init_size, core_size;
/* The size of the executable code in each section. */
unsigned int init_text_size, core_text_size;
/* Size of RO sections of the module (text+rodata) */
unsigned int init_ro_size, core_ro_size;
/* Arch-specific module values */
struct mod_arch_specific arch;
unsigned int taints; /* same bits as kernel:tainted */
#ifdef CONFIG_GENERIC_BUG
/* Support for BUG */
unsigned num_bugs;
struct list_head bug_list;
struct bug_entry *bug_table;
#endif
#ifdef CONFIG_KALLSYMS
/*
* We keep the symbol and string tables for kallsyms.
* The core_* fields below are temporary, loader-only (they
* could really be discarded after module init).
*/
Elf_Sym *symtab, *core_symtab;
unsigned int num_symtab, core_num_syms;
char *strtab, *core_strtab;
/* Section attributes */
struct module_sect_attrs *sect_attrs;
/* Notes attributes */
struct module_notes_attrs *notes_attrs;
#endif
/* The command line arguments (may be mangled). People like
keeping pointers to this stuff */
char *args;
#ifdef CONFIG_SMP
/* Per-cpu data. */
void __percpu *percpu; // 在percpu_modalloc中分配
unsigned int percpu_size; //在percpu_modalloc中赋值
#endif
#ifdef CONFIG_TRACEPOINTS
unsigned int num_tracepoints;
struct tracepoint * const *tracepoints_ptrs;
#endif
#ifdef HAVE_JUMP_LABEL
struct jump_entry *jump_entries;
unsigned int num_jump_entries;
#endif
#ifdef CONFIG_TRACING
unsigned int num_trace_bprintk_fmt;
const char **trace_bprintk_fmt_start;
#endif
#ifdef CONFIG_EVENT_TRACING
struct ftrace_event_call **trace_events;
unsigned int num_trace_events;
#endif
#ifdef CONFIG_FTRACE_MCOUNT_RECORD
unsigned int num_ftrace_callsites;
unsigned long *ftrace_callsites;
#endif
#ifdef CONFIG_MODULE_UNLOAD
/* What modules depend on me? */
struct list_head source_list;
/* What modules do I depend on? */
struct list_head target_list;
/* Who is waiting for us to be unloaded */
struct task_struct *waiter;
/* Destruction function. */
void (*exit)(void);
struct module_ref {
unsigned int incs;
unsigned int decs;
} __percpu *refptr;
#endif
#ifdef CONFIG_CONSTRUCTORS
/* Constructor functions. */
ctor_fn_t *ctors;
unsigned int num_ctors;
#endif
};
- 可执行目标文件在 ELF 头部的 e_phentsize 和 e_phnum 成员中给出其自身程序头部
的大小。
- 可执行文件与共享目标文件之间的段加载之间有一点不同。可执行文件的段通常包
含绝对代码,为了能够让进程正确执行,所使用的段必须是构造可执行文件时所使用的
虚拟地址。因此系统会使用 p_vaddr 作为虚拟地址