前のlinux: livepatchコードリーディングめも1 〜 patch側のデータ設定とpatchの登録まで - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモではさらっと流したklp_find_object_symbol()でpatch対象関数のアドレスを探すところを見てみる。
呼んでいる場所としてklp_find_verify_func_addr()を見てみると、
231 static int klp_find_verify_func_addr(struct klp_object *obj, 232 struct klp_func *func) 233 { 234 int ret; 235 236 #if defined(CONFIG_RANDOMIZE_BASE) 237 /* KASLR is enabled, disregard old_addr from user */ 238 func->old_addr = 0; 239 #endif 240 241 if (!func->old_addr || klp_is_module(obj)) 242 ret = klp_find_object_symbol(obj->name, func->old_name, 243 &func->old_addr); 244 else 245 ret = klp_verify_vmlinux_symbol(func->old_name, 246 func->old_addr); 247 248 return ret; 249 }
patchのカーネルモジュールでstruct klp_funcのold_addrを設定しなかった場合はここは0で初期化されているはずなので、klp_find_object_symbol()が使われます。
klp_find_object_symbol()はこのような関数で実際にシンボルを探すのはkallsyms_on_each_symbol()です。
172 static int klp_find_object_symbol(const char *objname, const char *name, 173 unsigned long *addr) 174 { 175 struct klp_find_arg args = { 176 .objname = objname, 177 .name = name, 178 .addr = 0, 179 .count = 0 180 }; 181 182 kallsyms_on_each_symbol(klp_find_callback, &args); 183 184 if (args.count == 0) 185 pr_err("symbol '%s' not found in symbol table\n", name); 186 else if (args.count > 1) 187 pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n", 188 args.count, name, objname); 189 else { 190 *addr = args.addr; 191 return 0; 192 } 193 194 *addr = 0; 195 return -EINVAL; 196 }
kallsyms_on_each_symbol()はkernel/kallsyms.cにあってコールバック関数と、コールバック関数に渡す引数を引数として取る関数です。
196 int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, 197 unsigned long), 198 void *data) 199 { 200 char namebuf[KSYM_NAME_LEN]; 201 unsigned long i; 202 unsigned int off; 203 int ret; 204 205 for (i = 0, off = 0; i < kallsyms_num_syms; i++) { 206 off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf)); 207 ret = fn(data, namebuf, NULL, kallsyms_addresses[i]); 208 if (ret != 0) 209 return ret; 210 } 211 return module_kallsyms_on_each_symbol(fn, data); 212 }
コールバック関数を使うと言っても処理はasyncでは無いですね。基本的にシンボル毎にコールバック関数を呼んでいく感じです。最後のmodule_kallsyms_on_each_symbol()はカーネルモジュール毎にシンボルを見ていって、kallsyms_on_each_symbol()と同じようにコールバック関数を呼んでいきます。
ここで使われているコールバック関数は当然livepatchが用意しているものでklp_find_callback()です。
147 static int klp_find_callback(void *data, const char *name, 148 struct module *mod, unsigned long addr) 149 { 150 struct klp_find_arg *args = data; 151 152 if ((mod && !args->objname) || (!mod && args->objname)) 153 return 0; 154 155 if (strcmp(args->name, name)) 156 return 0; 157 158 if (args->objname && strcmp(args->objname, mod->name)) 159 return 0; 160 161 /* 162 * args->addr might be overwritten if another match is found 163 * but klp_find_object_symbol() handles this and only returns the 164 * addr if count == 1. 165 */ 166 args->addr = addr; 167 args->count++; 168 169 return 0; 170 }
どんな場合もreturnするのは0ですね。これは欲しいシンボルじゃないけどエラーでもないので0を返すというところですかね。 もしくは全シンボルをチェックしたいからかな? 最初のほうのチェックで、モジュール名が入っている・いない(シンボルがカーネルモジュールにあるのかvmlinuxにあるのかというところで)、関数名が違うとかのチェックをして、これらをくぐり抜けるとそのシンボルのアドレスやカウンタがargs(1番目の引数のdata)に設定されます。
さて、klp_find_object_symbol()に戻ってkallsyms_on_each_symbol()が終わると以下の処理になります。
184 if (args.count == 0) 185 pr_err("symbol '%s' not found in symbol table\n", name); 186 else if (args.count > 1) 187 pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n", 188 args.count, name, objname); 189 else { 190 *addr = args.addr; 191 return 0; 192 } 193 194 *addr = 0;
まあ見たとおりですね。countが0ならシンボルが見つからなかった、逆にシンボルが1つ以上あった場合はどちらが正しいかlivepatchからだと解決できない(この場合、patch側でstruct klp_funcのold_addrを指定する必要がある)という場合はエラー。そうじゃなければ関数の引数の*addrにシンボルのアドレスを設定。
klp_find_object_symbol()の呼び出し側は下記のようにしているのでfunc->old_addrにシンボルのアドレスが設定されます。
242 ret = klp_find_object_symbol(obj->name, func->old_name, 243 &func->old_addr
ということで、シンボルのアドレスを探すのはkallsyms_on_each_symbol()とmodule_kallsyms_on_each_symbol()で各シンボルをコールバック関数で受け取って、そのアドレスが期待しているものかを調べるという方法で行われていました( ´ー`)フゥー...
Linuxコンテナー最新ツール Dockerを支える技術(日経BP Next ICT選書) 日経Linux技術解説書
- 作者: レッドハット,中井悦司
- 出版社/メーカー: 日経BP社
- 発売日: 2015/03/25
- メディア: Kindle版
- この商品を含むブログを見る