カーネルモジュールがロードされるアドレスって範囲決まってたはずだけど、自分で言っといてホントそうだったけ?とか思ったので確認。
@RKX1209 たしかmodule mapping spaceだと思った… https://t.co/4rH8h4G3rS
— int $0x03 (@masami256) 2016年10月4日
調べるのはLinux 4.8。 まず、Documentation/x86/x86_64/mm.txtを確認して、以下のところをチェック。
22 ffffffffa0000000 - ffffffffff5fffff (=1526 MB) module mapping space
で、この範囲にロードされるよねってとこです。
それでは、モジュールがどのように配置されるのかをコードで確認してみます。まずkernel/module.cのload_module()からみます。
この関数の最初のほうでlayout_and_allocate()を実行します。
3589 /* Figure out module layout, and allocate all the memory. */ 3590 mod = layout_and_allocate(info, flags);
layout_and_allocate()ではmove_module()という関数を呼びます。
3266 /* Allocate and move to the final place */ 3267 err = move_module(mod, info);
そして、ここで呼び出しているmodule_alloc()が実際にアドレスを指定してモジュール用のメモリを確保するところです。
3068 static int move_module(struct module *mod, struct load_info *info) 3069 { 3070 int i; 3071 void *ptr; 3072 3073 /* Do the allocs. */ 3074 ptr = module_alloc(mod->core_layout.size);
ただ、module_alloc()はアーキテクチャ依存な関数になります。module.cにあるmodule_alloc()はweak属性が付いているのでx86_64アーキテクチャにおいてはダミーです。
2688 void * __weak module_alloc(unsigned long size) 2689 { 2690 return vmalloc_exec(size); 2691 }
x86_64でのmodule_alloc()はarch/x86/kernel/module.cにあります。
79 void *module_alloc(unsigned long size) 80 { 81 void *p; 82 83 if (PAGE_ALIGN(size) > MODULES_LEN) 84 return NULL; 85 86 p = __vmalloc_node_range(size, MODULE_ALIGN, 87 MODULES_VADDR + get_module_load_offset(), 88 MODULES_END, GFP_KERNEL | __GFP_HIGHMEM, 89 PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE, 90 __builtin_return_address(0)); 91 if (p && (kasan_module_alloc(p, size) < 0)) { 92 vfree(p); 93 return NULL; 94 } 95 96 return p; 97 } 98
ここでは__vmalloc_node_range()を使ってメモリを確保しますが、この時にアドレスの範囲を指定します。__vmalloc_node_range()の3番目の引数は開始アドレス、4番目が終了アドレスです。
MODULES_VADDRとMODULES_ENDはarch/x86/include/asm/pgtable_64_types.hで定義されています。
67 #define MODULES_VADDR (__START_KERNEL_map + KERNEL_IMAGE_SIZE) 68 #define MODULES_END _AC(0xffffffffff000000, UL)
__START_KERNEL_mapとKERNEL_IMAGE_SIZEはarch/x86/include/asm/page_64_types.hで定義されています。
__START_KERNEL_mapはこうです。
46 #define __START_KERNEL_map _AC(0xffffffff80000000, UL)
KERNEL_IMAGE_SIZEは.configの設定次第ですが、うちはCONFIG_RANDOMIZE_BASEが有効なので60行目のほうを使います。
59 #if defined(CONFIG_RANDOMIZE_BASE) 60 #define KERNEL_IMAGE_SIZE (1024 * 1024 * 1024) 61 #else 62 #define KERNEL_IMAGE_SIZE (512 * 1024 * 1024) 63 #endif
なんで、こんな感じになります。
>>> hex(int("0xffffffff80000000", 16) + (1024 * 1024 * 1024)) '0xffffffffc0000000'
ということで、カーネルモジュールは0xffffffffc0000000〜0xffffffffff000000に置かれると。
実際にこれを確認するのは/proc/modulesを見れば良いので、こんな感じのスクリプトでチェックできます。
#!/usr/bin/env python with open('/proc/modules', 'r') as f: lines = f.readlines() for line in lines: data = line.split(' ') name = data[0] if (data[2] != '0'): addr = int(data[len(data) - 1].strip(), 16) if addr >= 0xffffffffc0000000 and addr < 0xffffffffff5fffff: print("%s:%x is in module mapping space " % (name, addr)) else: print("%s:%x is not in module mapping space" % (name, addr))
stack traceとって流れを確認するならこんなsystemtapのスクリプト(これくらいだとbccよりかんたんなのでこっちにしました)を動かして、適当なモジュールをinsmodすればOKです。
#!/usr/bin/env stap probe begin { printf("Start\n") } probe kernel.function("module_alloc") { printf("%s\n", symfileline(addr())); print_backtrace() exit() } probe end { printf("Done\n") }
こんな結果になります。
masami@saga:~/mod$ sudo stap -g module_alloc.stp Start 0xffffffffa705d8e0 0xffffffffa705d8e0 : module_alloc+0x0/0xd0 [kernel] 0xffffffffa7106cd6 : load_module+0x1576/0x2620 [kernel] (inexact) 0xffffffffa7204a31 : __vfs_read+0xe1/0x130 [kernel] (inexact) 0xffffffffa720578c : vfs_read+0x11c/0x130 [kernel] (inexact) 0xffffffffa720c238 : kernel_read_file+0x1e8/0x210 [kernel] (inexact) 0xffffffffa720c2a9 : kernel_read_file_from_fd+0x49/0x80 [kernel] (inexact) 0xffffffffa7107ff4 : SyS_finit_module+0xe4/0x120 [kernel] (inexact) 0xffffffffa75dc7f2 : entry_SYSCALL_64_fastpath+0x1a/0xa4 [kernel] (inexact) Done
ということで、一応合ってたようですε-(´∀`*)ホッ
[改訂新版]プロのためのLinuxシステム構築・運用技術 (Software Design plus)
- 作者: 中井悦司
- 出版社/メーカー: 技術評論社
- 発売日: 2016/09/22
- メディア: 大型本
- この商品を含むブログを見る