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;
}





