內核模塊機制API —— __module_address和__module_text_address
來自專欄 Linux 4.x 內核API淺析1 人贊了文章
文件包含:
#include <linux/module.h>
函數定義和功能:
在內核中linux-4.9.109/kernel/module.c
root@ubuntu:/home/user/Desktop/Personal/zhihu/LinuxKernel/kernel/linux-4.9.109/kernel# lsacct.c bpf cpuset.c fork.c kcmp.c kprobes.c module_signing.c ptrace.c smp.c time utsname.casync.c capability.c crash_dump.c freezer.c Kconfig.freezer ksysfs.c notifier.c range.c softirq.c torture.c utsname_sysctl.caudit.c cgroup.c cred.c futex.c Kconfig.hz kthread.c nsproxy.c rcu stacktrace.c trace watchdog.cauditfilter.c cgroup_freezer.c debug futex_compat.c Kconfig.locks latencytop.c padata.c reboot.c stop_machine.c tracepoint.c watchdog_hld.caudit_fsnotify.c cgroup_pids.c delayacct.c gcov Kconfig.preempt livepatch panic.c relay.c sys.c tsacct.c workqueue.caudit.h compat.c dma.c groups.c kcov.c locking params.c resource.c sysctl_binary.c ucount.c workqueue_internal.hauditsc.c configs elfcore.c hung_task.c kexec.c Makefile pid.c sched sysctl.c uid16.caudit_tree.c configs.c events irq kexec_core.c membarrier.c pid_namespace.c seccomp.c sys_ni.c up.caudit_watch.c context_tracking.c exec_domain.c irq_work.c kexec_file.c memremap.c power signal.c taskstats.c user.cbacktracetest.c cpu.c exit.c jump_label.c kexec_internal.h module.c printk smpboot.c task_work.c user_namespace.cbounds.c cpu_pm.c extable.c kallsyms.c kmod.c module-internal.h profile.c smpboot.h test_kprobes.c user-return-notifier.c
結構體struct module的定義:
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. */#ifdef CONFIG_SYSFS struct mutex param_lock;#endif 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#ifdef CONFIG_MODULE_SIG /* Signature was verified. */ bool sig_ok;#endif bool async_probe_requested; /* 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); /* Core layout: rbtree is accessed frequently, so keep together. */ struct module_layout core_layout __module_layout_align; struct module_layout init_layout; /* 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 /* Protected by RCU and/or module_mutex: use rcu_dereference() */ struct mod_kallsyms *kallsyms; struct mod_kallsyms core_kallsyms; /* 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; unsigned int percpu_size;#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 trace_event_call **trace_events; unsigned int num_trace_events; struct trace_enum_map **trace_enums; unsigned int num_trace_enums;#endif#ifdef CONFIG_FTRACE_MCOUNT_RECORD unsigned int num_ftrace_callsites; unsigned long *ftrace_callsites;#endif#ifdef CONFIG_LIVEPATCH bool klp; /* Is this a livepatch module? */ bool klp_alive; /* Elf information */ struct klp_modinfo *klp_info;#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; /* Destruction function. */ void (*exit)(void); atomic_t refcnt;#endif#ifdef CONFIG_CONSTRUCTORS /* Constructor functions. */ ctor_fn_t *ctors; unsigned int num_ctors;#endif} ____cacheline_aligned;
定義:
struct module* __module_address(unsigned long addr);struct module* __module_text_address(unsigned long addr);
功能:
函數__module_address()根據給定的內存地址addr,獲得該內存地址所在的模塊。
函數__module_text_address()獲得的是一個模塊指針,但必須滿足addr所示的內存地址落在該模塊的代碼中。
示例:
#include<linux/module.h>#include<linux/init.h>MODULE_LICENSE("GPL");MODULE_AUTHOR("viz.xu, xujiweigo@163.com");static int __module_address_init(void);static void __module_address_exit(void);static int var = 10;int test_module(void){ printk("enter test_module!
");return 0;}static int __init __module_address_init(void){ printk("__module_address init!
"); struct module* ret = NULL; unsigned long addr = (unsigned long)test_module; /* must lock before calling the __module_address */ preempt_disable(); // ret = (struct module *)__module_address(addr); preempt_enable(); printk("__module_address find the module!
"); if(ret != NULL){ printk("module name = %s
",ret->name); // module name printk("module state = %d
",ret->state); // module state printk("module refs = %d
",module_refcount(ret)); // the references of the module } else{ printk("module address get NULL!
"); } printk("__module_test_address find the module!
"); preempt_disable(); ret = (struct module*)__module_text_address(addr); preempt_enable(); if(ret != NULL){ printk("module name = %s
",ret->name); // module name printk("module state = %d
",ret->state); // module state printk("module refs = %d
",module_refcount(ret)); // the references of the module } else{ printk("module address get NULL!
"); } printk("__module_text_address, get from var!
"); addr = (unsigned long)&var; preempt_disable(); ret = __module_text_address(addr); preempt_enable(); if(ret != NULL){ printk("module name = %s
",ret->name); // module name printk("module state = %d
",ret->state); // module state printk("module refs = %d
",module_refcount(ret)); // the references of the module } else{ printk("module address get NULL!
"); } printk("__module_address ok!
");return 0;}static void __exit __module_address_exit(void){ printk("__module_address exit!
");return;}module_init(__module_address_init);module_exit(__module_address_exit);
insmod模塊後dmesg所示信息:
[ 7748.188637] __module_address init![ 7748.188638] __module_address find the module![ 7748.188639] module name = viz_moduletest1[ 7748.188639] module state = 1[ 7748.188640] module refs = 1[ 7748.188640] __module_test_address find the module![ 7748.188640] module name = viz_moduletest1[ 7748.188641] module state = 1[ 7748.188641] module refs = 1[ 7748.188641] __module_text_address, get from var![ 7748.188642] module address get NULL![ 7748.188642] __module_address ok!
結果分析:
__module_address 和 __module_text_address內核API,可以通過模塊內存地址獲取模塊信息。
如果內存所在地址屬於該模塊,那麼如上述函數 test_module 屬於模塊viz_moduletest1,而static變數var不屬於該模塊,因這全局靜態變數處於數據段中,所以dmesg顯示module address get NULL
推薦閱讀:
※為什麼Google play 沒有手機 QQ 標準版?
※如何讓Scaladoc鏈接到外部API?
※一些國內值得關注的API合集
※用中文命名API的意義和途徑
※框架,依賴注入,以及介面