作者:luohandsome
事实上"linux-gate.so"的“载入”不是由/lib/ld-linux.so.2完成的。而是在系统启动时,由内核函数sysenter_setup()(start_kernel->check_bugs->identify_boot_cpu->sysenter_setup)完成的。该函数先获得一个空的物理页syscall_page,然后把vsyscall-sysenter.so(或者arch/i386/kernel/vsyscall-int80.so,看CPU是否支持sysenter)的内容拷贝到syscall_page(从vsyscall_sysenter_start到vsyscall_sysenter_end)。最后把syscall_page映射到虚拟地址ffffe000-fffff000,正好是一个页。
那么vsyscall-sysenter.so是什么时候载入内核的呢?查找一下vsyscall_sysenter_start和vsyscall_sysenter_end的定义,可以知道他们定义在arch/i386/kernel/vsyscall.S中,是作为内核的一部分来编译的,只是与一般的内核.o文件不同,他们是.so文件而已。
nm vmlinux|grep vsyscall_sysenter:
c13d1b1a T vsyscall_sysenter_end
c13d1272 T vsyscall_sysenter_start
最后有一个问题:
cat /proc/4525/maps :
....
ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]
看上去没有可执行权限,而且这段代码在内核态。但是objdump的结果:
ffffe400 <__kernel_vsyscall>:
ffffe400: 51 push %ecx
ffffe401: 52 push %edx
ffffe402: 55 push %ebp
ffffe403: 89 e5 mov %esp,%ebp
ffffe405: 0f 34 sysenter
...
应该是由用户态进程执行的。那么,用户态进程如何执行内核态的代码,又如果执行没有可执行权限的代码?
在sysenter_setup->gate_vma_init()中,内核把ffffe000-fffff0000设置为
可读,可执行。
gate_vma.vm_flags = VM_READ | VM_MAYREAD | VM_EXEC | VM_MAYEXEC;
gate_vma.vm_page_prot = __P101;
这样,应用程序就能合法访问vdso这个段了。
不过,我不明白为什么在cat /proc/$pid/maps时,里面显示这个段没有读写和执行权限(“---p”)
后来发现。我看的代码是2.6.23的,但是运行的内核是2.6.13的。在2.6.13里gate_vma.vm_flags = 0。不过这不妨碍对这个区域的访问。