flat assembler
Message board for the users of flat assembler.
![]() |
Author |
|
nocona 18 Aug 2007, 04:25
Linux LKM (loadable kernel module) is a relocateable elf file. This type of file can be created directly by using "format elf" or format elf64" fasm's directive. To build a valid LKM with fasm, we need to create at least 2 sections : one for our code/data and another is a special section named ".gnu.linkonce.this_module", which only content is the "module" structure. Within this structure, only 3 members need to be initialized at assembly time : the "name", "init" and "exit" members.
The "name" member is the internal name of our module (must be unique at runtime). The "init" member must be set to our module's initialization routine address, and its C prototype is "int init(void)". The "exit" member should be set to our module's termination routine address, and its C prototype is "void exit(void)" If we read the definition of "struct module" in kernel source header "module.h", we can see that it will be different across different kernel configuration. The offset of "init" member also differs in different kernel configuration. Also, if we configured our kernel to not support module unloading, the "exit" member should not exist in our module strcuture. In particular, these kernel configuration affect our "module" structure : CONFIG_PREEMPT CONFIG_SMP CONFIG_DEBUG_SPINLOCK CONFIG_DEBUG_LOCK_ALLOC CONFIG_GENERIC_BUG CONFIG_MODULE_UNLOAD CONFIG_NR_CPUS CONFIG_X86_L1_CACHE_SHIFT CONFIG_KALLSYMS Apart than this mandatory section, there are other sections that our module can have, depending on our needs. The '.modinfo" section is the way to provide information about our module (e.g can be retrieve using "modinfo" program). It contains one or more <tag>=<value> strings. For example, to provide license information, you can include the null terminated string "license=whatever your license" in this section. Note that absence of "license" info from our module make the kernel tainted (i.e the kernel assume our module isn't GPL compatible). Although the ".modinfo" section is not mandatory, but when it exist, there is one information in this section that, when inspected by kernel can cause loading failure if the check failed. It is the "vermagic" info. When this info exist, it must be the same as VERMAGIC_STRING, which is defined as Code: #define VERMAGIC_STRING UTS_RELEASE " " MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT MODULE_VERMAGIC_UNLOAD MODULE_ARCH_VERMAGIC for example, in my kernel 2.6.22-kamikaze5 configured with smp and module unloading support, I can put "vermagic=2.6.22-kamikaze5 SMP mod_unload " in ".modinfo" section of my module. Note that modprobe can force loading of module where its vermagic is different from the running kernel using the option --force or --force-vermagic. If our module accept parameter, then it must have the "__param" section. This section consists of array of kernel_param structure, which is defined like below (example for 64-bit) Code: struc kernel_param { .name dq ? .perm dd ? align 8 .set dq ? .get dq ? .arg dq ? } it is easy to understand this structure, where "name" is the address of null terminated string, specifying the name of this parameter. "perm" is the file permission. This is relevant if we allow users to access this parameter in /sys/module/<module_name>/parameters. "set" and "get" is the setter and getter function for the variable, respectively. The kernel already export some predefined setter and getter functions for predefined types, e.g if our variable is of type int, then we should set getter and setter function to the "param_get_int" and "param_set_int" symbols. "arg" is the address of our variable. Whenever we setup a kernel_param structure, we usually also setup a "parmtype=<param_name>:<type>" info in ".modinfo" section, but it's not mandatory. Others section which is also useful is "__ksymtab" section which allows us to export symbols to be used by other modules. There also "__ksymtab_gpl", "__ksymtab_gpl_future", "__ksymtab_unused" and "__ksymtab_unused_gpl" that has the same sections layout but each has different meanings (for example, exported symbols in __ksymtab_gpl can only be used by others that are licensed under GPL or compatible). The example code below show an example of a very simple lkm written in fasm. Due to fasm's bug as described here, this code can only be run (loaded) correctly by applying the rela patch here to the fasm's code and assembling it with the new fasm binary. What this code does is printing the value of its 2 parameters "lkm_param1" (of type int) and "lkm_param2" (of type string) at init and exit routine. Assemble normally with patched fasm, and insmod the object file to load the module, e.g "fasm lkm.asm" then "insmod lkm.o". You can change the value of the 2 parameter with insmod, e.g "insmod lkm.o lkm_param1=99 lkm_param2="anything"" or at runtime using the /sys interface, e.g "echo 1 > /sys/module/lkm/parameters/lkm_param1". Note that the kernel configuration variable at the beginning of the code must match to your kernel configuration) and this is strictly for 64-bit machine. Code: MODULE_NAME_LEN64 = 56 KOBJ_NAME_LEN64 = 20 CONFIG_PREEMPT=1 CONFIG_SMP=1 CONFIG_DEBUG_SPINLOCK=0 CONFIG_DEBUG_LOCK_ALLOC=0 CONFIG_GENERIC_BUG=1 CONFIG_MODULE_UNLOAD=1 CONFIG_NR_CPUS=2 CONFIG_X86_L1_CACHE_SHIFT=6 CONFIG_KALLSYMS=1 MODULE_REF64_ALIGNMENT = 1 shl CONFIG_X86_L1_CACHE_SHIFT struc mod_arch_specific64 {} struc list_head64 { .next dq ? .prev dq ? .SIZE=$-.next } struc module_kobject64 { .kobj kobject64 .mod dq ? .drivers_dir dq ? .SIZE=$-.kobj } struc kobject64 { .k_name dq ? .name rb KOBJ_NAME_LEN64 ;align 4:0 .kref kref64 .entry list_head64 .parent dq ? .kset dq ? .ktype dq ? .dentry dq ? .poll wait_queue_head64 } struc kref64 {.refcount dd ?} struc module_ref64 { .count dq ? align MODULE_REF64_ALIGNMENT .SIZE = $-.count } struc wait_queue_head64 { .lock spinlock_t64 .task_list list_head64 } struc spinlock_t64 { .raw_lock dd ? if CONFIG_PREEMPT=1 & CONFIG_SMP=1 .break_lock dd ? end if if CONFIG_DEBUG_SPINLOCK=1 .magic dd ? .owner_cpu dd ? align 8:0 .owner dq ? end if if CONFIG_DEBUG_LOCK_ALLOC .dep_map lockdep_map64 end if } struc lockdep_map64 { .key dq ? .class_cache dq ? .name dq ? } struc module64 name, init, cleanup { .state dq ? .list list_head64 .name db name rb MODULE_NAME_LEN64-($-.name) align 8:0 .mkobj module_kobject64 .param_attrs dq ? .modinfo_attrs dq ? .version dq ? .srcversion dq ? .holders_dir dq ? .syms dq ? .num_syms dd ? align 8:0 .crcs dq ? .gpl_syms dq ? .gpl_num_syms dd ? align 8:0 .gpl_crcs dq ? .unused_syms dq ? .unused_num_syms dd ? align 8:0 .unused_crcs dq ? .unused_gpl_syms dq ? .unused_gpl_num_syms dd ? align 8:0 .unused_gpl_crcs dq ? .gpl_future_syms dq ? .gpl_future_num_syms dd ? align 8:0 .gpl_future_crcs dq ? .num_exentries dd ? align 8:0 .extable dq ? .init dq init+0 .module_init dq ? .module_core dq ? .init_size dd ? .core_size dd ? .init_text_size dq ? .core_text_size dq ? .unwind_info dq ? .arch mod_arch_specific64 .unsafe dd ? .taints dd ? if CONFIG_GENERIC_BUG=1 .bug_list list_head64 .bug_table dq ? .num_bugs dq ? end if if CONFIG_MODULE_UNLOAD=1 if MODULE_REF64_ALIGNMENT > 8 align MODULE_REF64_ALIGNMENT end if .ref module_ref64 rb .ref.SIZE*(CONFIG_NR_CPUS-1) .modules_which_use_me list_head64 .waiter dq ? .exit dq cleanup+0 end if if CONFIG_KALLSYMS=1 .symtab dq ? .num_symtab dq ? .strtab dq ? .sect_attrs dq ? end if .percpu dq ? .arg dq ? if MODULE_REF64_ALIGNMENT > 8 align MODULE_REF64_ALIGNMENT end if .SIZE=$-.state } ;/-----------------------------------------------------------------------------\ ; Description : align offset according to alignment value and fill byte ; specified by valueSpec. Format of usage is ; align <alignValue>[':'<fillByte>] ; e.g ; align 32:0x90 ; align 4 ; default for fillByte is 0. ;\-----------------------------------------------------------------------------/ macro align @valueSpec* { local ..pad match .value:.byte, @valueSpec \{ virtual align .value ..pad = $ - $$ end virtual times ..pad db .byte and 0xff define do \} match =do .value, do @valueSpec \{ virtual align .value ..pad = $ - $$ end virtual rb ..pad \} restore do } macro extern [@sym] {extrn @sym} extrn fix extern format elf64 extrn printk, param_set_int, param_get_int, param_get_string, \ param_set_copystring section ".text" executable align 4 init_module: mov esi, [lkm_param1] mov rdi, msg_init call printk mov rdi, msg_init2 mov rsi, lkm_param2_buffer call printk xor eax, eax ret cleanup: jmp init_module msg_init db "<1>lkm_param1 = %d", 10, 0 msg_init2 db "<1>lkm_param2 = %s", 10, 0 param_name_lkm_param1 db "lkm_param1", 0 param_name_lkm_param2 db "lkm_param2", 0 if MODULE_REF64_ALIGNMENT>8 section ".gnu.linkonce.this_module" align MODULE_REF64_ALIGNMENT else section ".gnu.linkonce.this_module" align 8 end if __this_module module64 "lkm2", init_module, cleanup section ".modinfo" align 1 ;db "license=foo", 0 db "author=nocona", 0 db "vermagic=2.6.22-kamikaze5 SMP mod_unload ", 0 db "description=test module written using fasm", 0 section "__param" align 8 lkm_param1_parameter: dq param_name_lkm_param1 dd 644o align 8:0 dq param_set_int dq param_get_int dq lkm_param1 lkm_param2_parameter: dq param_name_lkm_param2 dd 644o align 8:0 dq param_set_copystring dq param_get_string dq lkm_param2 ;this is the address of kparam_string structure section ".data" writeable align 8 lkm_param2_buffer db "default string", 0 rb 32-($-lkm_param2_buffer) align 8 lkm_param1 dd 1 align 8 lkm_param2 dd 32 align 8:0 dq lkm_param2_buffer |
|||
![]() |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2023, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.