VDSO_COMPTの/proc/pid/mapsのvdso
CONFIG_X86_32の/proc/sys/vm/vdso_enabled=2(VDSO_COMPT)の時、カーネル空間に割り当てられたシステムコールは、ユーザ空間にマップされない。しかし/proc/pid/mapsで見ると、この処理はタスクのmmにリストされているvm_area_structを、順次表示しているにかかわらず、VDSO部も表示される。
まず、/proc/pid/mapsの処理の概略から。
/proc/pid/mapsが作成されると(inodeが作成されると)、そのiノードのコールバック関数に、proc_maps_operationsが設定される。このiノードがオープンされると、このコールバック関数の.open=maps_open()がコールされ、file->private_dataにproc_pid_maps_opコールバック関数が設定されることで、file_operationsのシーケンスファイル読み込み関数で、/proc/pid/mapsにかかる処理が行われることで実現する。
まず、/proc/pid/mapsの処理の概略から。
/proc/pid/mapsが作成されると(inodeが作成されると)、そのiノードのコールバック関数に、proc_maps_operationsが設定される。このiノードがオープンされると、このコールバック関数の.open=maps_open()がコールされ、file->private_dataにproc_pid_maps_opコールバック関数が設定されることで、file_operationsのシーケンスファイル読み込み関数で、/proc/pid/mapsにかかる処理が行われることで実現する。
static const struct seq_operations proc_pid_maps_op = { .start = m_start, .next = m_next, .stop = m_stop, .show = show_map }; const struct file_operations proc_maps_operations = { .open = maps_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private, }; static int maps_open(struct inode *inode, struct file *file) { return do_maps_open(inode, file, &proc_pid_maps_op); }m_start()は読み込み開始時にコールされ、読み込みプロセスIDのmm_structを取得して、セマフォを取得する。そしてget_gate_vma()をコールして、VDSOのvm_area_structを取得し、それをpriv->tail_vma = tail_vmaとすることで、 タスクのmm_structに登録されてないVDSOケースでも、/proc/pid/mapsで表示されるようになっている。
static void *m_start(struct seq_file *m, loff_t *pos) { struct proc_maps_private *priv = m->private; unsigned long last_addr = m->version; struct mm_struct *mm; struct vm_area_struct *vma, *tail_vma = NULL; loff_t l = *pos; priv->task = NULL; priv->tail_vma = NULL; if (last_addr == -1UL) return NULL; priv->task = get_pid_task(priv->pid, PIDTYPE_PID); if (!priv->task) return ERR_PTR(-ESRCH); mm = mm_for_maps(priv->task); if (!mm || IS_ERR(mm)) return mm; down_read(&mm->mmap_sem); tail_vma = get_gate_vma(priv->task->mm); priv->tail_vma = tail_vma; vma = find_vma(mm, last_addr); if (last_addr && vma) { vma = vma->vm_next; goto out; } vma = NULL; if ((unsigned long)l < mm->map_count) { vma = mm->mmap; while (l-- && vma) vma = vma->vm_next; goto out; } if (l != mm->map_count) tail_vma = NULL; /* After gate vma */ out: if (vma) return vma; m->version = (tail_vma != NULL)? 0: -1UL; up_read(&mm->mmap_sem); mmput(mm); return tail_vma; }VDSOを割り当てるarch_setup_additional_pages()で、current->mm->context.vdso = (void *)addrとしていて、そのアドレスがVDSO_HIGH_BASEの時、VDSO_COMPATと言うこと。で、gate_vmaが返される。
struct vm_area_struct *get_gate_vma(struct mm_struct *mm) { if (mm && mm->context.vdso == (void *)VDSO_HIGH_BASE) return &gate_vma; return NULL; }なお、gate_vmaは、vdso32-setup時に以下の様に作成されている。
static int __init gate_vma_init(void) { gate_vma.vm_mm = NULL; gate_vma.vm_start = FIXADDR_USER_START; gate_vma.vm_end = FIXADDR_USER_END; gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC; gate_vma.vm_page_prot = __P101; gate_vma.vm_flags |= VM_ALWAYSDUMP; return 0; }