2009年3月24日火曜日

仕方ないのでmod probeを読むことにした

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
kernel/kmod.c



extern int max_threads;

static struct workqueue_struct *khelper_wq;

#ifdef CONFIG_MODULES

/*
modprobe_path is set via /proc/sys.
*/
char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";

pathがhard codeされている。


/**
* request_module - try to load a kernel module
* @fmt: printf style format string for the name of the module
* @...: arguments as specified in the format string
*
* Load a module using the user mode module loader. The function returns
* zero on success or a negative errno code on failure. Note that a
* successful module load does not mean the module did not then unload
* and exit on an error of its own. Callers must check that the service
* they requested is now available not blindly invoke it.
*
* If module auto-loading support is disabled then this function
* becomes a no-operation.
*/

成功時0、失敗時負数。


int request_module(const char *fmt, ...)
{
va_list args;
char module_name[MODULE_NAME_LEN];
unsigned int max_modprobes;
int ret;
char *argv[] = { modprobe_path, "-q", "--", module_name, NULL };
static char *envp[] = { "HOME=/",
"TERM=linux",
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
NULL };
static atomic_t kmod_concurrent = ATOMIC_INIT(0);
#define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */
static int kmod_loop_msg;

va_start(args, fmt);
ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
va_end(args);
if (ret >= MODULE_NAME_LEN)
return -ENAMETOOLONG;
引数のパース


/* If modprobe needs a service that is in a module, we get a recursive
* loop. Limit the number of running kmod threads to max_threads/2 or
* MAX_KMOD_CONCURRENT, whichever is the smaller. A cleaner method
* would be to run the parents of this process, counting how many times
* kmod was invoked. That would mean accessing the internals of the
* process tables to get the command line, proc_pid_cmdline is static
* and it is not worth changing the proc code just to handle this case.
* KAO.
*
* "trace the ppid" is simple, but will fail if someone's
* parent exits. I think this is as good as it gets. --RR
*/

ここからが肝、というか、call_usermodehelperだけやんけ・・・

max_modprobes = min(max_threads/2, MAX_KMOD_CONCURRENT);
atomic_inc(&kmod_concurrent);
if (atomic_read(&kmod_concurrent) > max_modprobes) {
/* We may be blaming an innocent here, but unlikely */
if (kmod_loop_msg++ < 5)
printk(KERN_ERR
"request_module: runaway loop modprobe %s\n",
module_name);
atomic_dec(&kmod_concurrent);
return -ENOMEM;
}

ret = call_usermodehelper(modprobe_path, argv, envp, 1);
atomic_dec(&kmod_concurrent);
return ret;
}
EXPORT_SYMBOL(request_module);
#endif /* CONFIG_MODULES */

man modprobeによると、

modprobe intelligently adds or removes a module from the Linux kernel: note that
for convenience, there is no difference between _ and - in module names. modprobe
looks in the module directory /lib/modules/‘uname -r‘ for all the modules and other
files, except for the optional /etc/modprobe.conf configuration file and /etc/mod-
probe.d directory (see modprobe.conf(5)).

Note that this version of modprobe does not do anything to the module itself: the
work of resolving symbols and understanding parameters is done inside the kernel.
So module failure is sometimes accompanied by a kernel message: see dmesg(8).

modprobe expects an up-to-date modules.dep file, as generated by depmod (see dep-
mod(8)). This file lists what other modules each module needs (if any), and mod-
probe uses this to add or remove these dependencies automatically. See mod-
ules.dep(5)).

だそうな。全然ほしい情報が無い。

request_moduleの引数に渡しているものがわかっているので、それでgrep。

[nori@asama]~/Desktop/work/kernel/linux-2.6-allstable% grep -R 'request_module("binfmt' .
./fs/exec.c: request_module("binfmt-%04x", *(unsigned short *)(&bprm->buf[2]));


呼んでいる関数はこいつ。

/*
* cycle the list of binary formats handler, until one recognizes the image
*/
int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
{
unsigned int depth = bprm->recursion_depth;
int try,retval;
struct linux_binfmt *fmt;

retval = security_bprm_check(bprm);

この関数の終わりのほうで、

if (retval != -ENOEXEC || bprm->mm == NULL) {
break;
#ifdef CONFIG_MODULES
} else {
#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
if (printable(bprm->buf[0]) &&
printable(bprm->buf[1]) &&
printable(bprm->buf[2]) &&
printable(bprm->buf[3]))
break; /* -ENOEXEC */
request_module("binfmt-%04x", *(unsigned short *)(&bprm->buf[2]));
#endif
}
}
return retval;
}

んで、bprmとはなにかというと、これ。

/*
* This structure is used to hold the arguments that are used when loading binaries.
*/
struct linux_binprm{
char buf[BINPRM_BUF_SIZE];
#ifdef CONFIG_MMU
struct vm_area_struct *vma;
#else
# define MAX_ARG_PAGES 32
struct page *page[MAX_ARG_PAGES];
#endif
struct mm_struct *mm;
unsigned long p; /* current top of mem */
unsigned int sh_bang:1,
misc_bang:1,
cred_prepared:1,/* true if creds already prepared (multiple
* preps happen for interpreters) */
cap_effective:1;/* true if has elevated effective capabilities,
* false if not; except for init which inherits
* its parent's caps anyway */
#ifdef __alpha__
unsigned int taso:1;
#endif
unsigned int recursion_depth;
struct file * file;
struct cred *cred; /* new credentials */
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
unsigned int per_clear; /* bits to clear in current->personality */
int argc, envc;
char * filename; /* Name of binary as seen by procps */
char * interp; /* Name of the binary really executed. Most
of the time same as filename, but could be
different for binfmt_{misc,script} */
unsigned interp_flags;
unsigned interp_data;
unsigned long loader, exec;
};

一方で、binfmt_なファイル名を検索するとこうなる。

[nori@asama]~/Desktop/work/kernel/linux-2.6-allstable% find . | grep -e "binfmt_.*\.c$"
./arch/alpha/kernel/binfmt_loader.c
./arch/parisc/kernel/binfmt_elf32.c
./arch/mips/kernel/binfmt_elfn32.c
./arch/mips/kernel/binfmt_elfo32.c
./arch/ia64/ia32/binfmt_elf32.c
./fs/binfmt_som.c
./fs/binfmt_elf_fdpic.c
./fs/binfmt_elf.c
./fs/binfmt_misc.c
./fs/binfmt_em86.c
./fs/compat_binfmt_elf.c
./fs/binfmt_script.c
./fs/binfmt_flat.c
./fs/binfmt_aout.c

とりあえず、fs/binfmt_aout.cを見ることに。ここでbufが初期化される。

static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
{
struct exec ex;
unsigned long error;
unsigned long fd_offset;
unsigned long rlim;
int retval;

ex = *((struct exec *) bprm->buf); /* exec-header */
if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
N_TRSIZE(ex) || N_DRSIZE(ex) ||
i_size_read(bprm->file->f_path.dentry->d_inode) < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
return -ENOEXEC;
}

struc execはこんな感じ。(x86の場合)

struct exec
{
unsigned int a_info; /* Use macros N_MAGIC, etc for access */
unsigned a_text; /* length of text, in bytes */
unsigned a_data; /* length of data, in bytes */
unsigned a_bss; /* length of uninitialized data area for file, in bytes */
unsigned a_syms; /* length of symbol table data in file, in bytes */
unsigned a_entry; /* start address */
unsigned a_trsize; /* length of relocation info for text, in bytes */
unsigned a_drsize; /* length of relocation info for data, in bytes */
};

XXMAGICを解読して、464が何であるかを知る必要がありそうだ。
あー、だめだ。aoutじゃないし。そもそもになんだかわかってない。validじゃないtypeが来ているので、その起源が知りたい可能性大。

で、マクロはinclude/Linux/a.out.hで定義されている。

/* Code indicating object file or impure executable. */
#define OMAGIC 0407
/* Code indicating pure executable. */
#define NMAGIC 0410
/* Code indicating demand-paged executable. */
#define ZMAGIC 0413
/* This indicates a demand-paged executable with the header in the text.
The first page is unmapped to help trap NULL pointer references */
#define QMAGIC 0314

/* Code indicating core file. */
#define CMAGIC 0421

1 件のコメント:

nori さんのコメント...
このコメントは投稿者によって削除されました。