- 论坛徽章:
- 0
|
当你在比较新的linux系统上使用ldd工具的时候,你会发现有一个奇怪的东东:linux-gate.so.1:
- ldd /bin/sh
- linux-gate.so.1 => (0xffffe000)
- libdl.so.2 => /lib/libdl.so.2 (0xb7fb2000)
- libc.so.6 => /lib/libc.so.6 (0xb7e7c000)
- /lib/ld-linux.so.2 (0xb7fba000)
复制代码
[译者注:作者的sh使用新版本glibc编译过,如果是就版本,比如2.3.3,则见到的可能会有些差别:
- [rick@Fedora-Core test]$ ldd /bin/sh
- linux-gate.so.1 => (0xffffe000)
- libtermcap.so.2 => /lib/libtermcap.so.2 (0x00ca0000)
- libdl.so.2 => /lib/libdl.so.2 (0x00a91000)
- libc.so.6 => /lib/tls/libc.so.6 (0x00943000)
- /lib/ld-linux.so.2 (0x00926000)
复制代码
那这有什么奇怪的呢?只是一个动态库,是吗?
根据很多动态库的定义,是有几分像.从ldd的结果看,ldd找不到这个文件.事实上,无论是手动去找,或者借助一些软件工具,都无法找到相关文件.有时,当用户去寻找这个不存在的文件会很迷茫.你可以明确的告诉他们系统中根本没有这样一个文件存在,他只是一个虚拟的动态共享库,是由内核导出的存在于每个进程空间中的一个共享对象:
- cat /proc/self/maps
- 08048000-0804c000 r-xp 00000000 08:03 7971106 /bin/cat
- 0804c000-0804d000 rwxp 00003000 08:03 7971106 /bin/cat
- 0804d000-0806e000 rwxp 0804d000 00:00 0 [heap]
- b7e88000-b7e89000 rwxp b7e88000 00:00 0
- b7e89000-b7fb8000 r-xp 00000000 08:03 8856588 /lib/libc-2.3.5.so
- b7fb8000-b7fb9000 r-xp 0012e000 08:03 8856588 /lib/libc-2.3.5.so
- b7fb9000-b7fbc000 rwxp 0012f000 08:03 8856588 /lib/libc-2.3.5.so
- b7fbc000-b7fbe000 rwxp b7fbc000 00:00 0
- b7fc2000-b7fd9000 r-xp 00000000 08:03 8856915 /lib/ld-2.3.5.so
- b7fd9000-b7fdb000 rwxp 00016000 08:03 8856915 /lib/ld-2.3.5.so
- bfac3000-bfad9000 rw-p bfac3000 00:00 0 [stack]
- ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]
复制代码
这里,cat 命令打印出了自身的内存映射情况.标记有[vdso]的那行就是这个进程的linux-gate.so.1对象,她是从0xffffe000开始映射的一页.程序可以从ELF辅助向量表的AT_SYSINFO项得到这个共享对象的载入地址.辅助向量(auxv)其实就是一个指针数组,像argv,env一样传给一个新进程.(关于辅助向量,在拙作stack explore中也提到过).
理论上,这个地址(linux-gate.so.1的载入地址)在每个进程之间可以不相同,但是据我所知,内核总是将他映射到一个固定的地址(其实就是 0xffffe000).上面例子的实验平台是:x86 cpu,32位地址空间,每页4KB,这样0xffffe000就是倒数第二页.最后一页被保留用来捕获不合法的指针访问,比如:解引用一个null指针,或者由mmap返回的映射失败的指针.
因为所有进程在相同的地址共享这个对象(linux-gate.so.1),所以如果我们想要仔细看看这个对象,可以很方便的得到他.比如,我们可以使用 dd工具(注意不要选择linux-gate.so.1作为文件名,以避免使以前假定的不存在的文件存在,也就是说,本来linux-gate.so.1 事实上是不存在的,如果你使用了这个名字,那么就令这个文件存在了,这样不大好):
- dd if=/proc/self/mem of=linux-gate.dso bs=4096 skip=1048574 count=1
- 1+0 records in
- 1+0 records out
复制代码
我们跳过了1048574,是因为一共有2^20=1048576个虚拟页面,而我们需要的是倒数第二页.得到的结果(linux-gate.dso文件)跟其他的ELF共享库文件看起来是一样的:
- file -b linux-gate.dso
- ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), stripped
- objdump -T linux-gate.dso
- linux-gate.dso: file format elf32-i386
- DYNAMIC SYMBOL TABLE:
- ffffe400 l d .text 00000000
- ffffe460 l d .eh_frame_hdr 00000000
- ffffe484 l d .eh_frame 00000000
- ffffe608 l d .useless 00000000
- ffffe400 g DF .text 00000014 LINUX_2.5 __kernel_vsyscall
- 00000000 g DO *ABS* 00000000 LINUX_2.5 LINUX_2.5
- ffffe440 g DF .text 00000007 LINUX_2.5 __kernel_rt_sigreturn
- ffffe420 g DF .text 00000008 LINUX_2.5 __kernel_sigreturn
复制代码
这些符号是rt_sigreturn/sigreturn函数的入口点,同时为虚拟系统调用服务.在x86平台上,linux-gate.so.1开始的时候被称作linux-vsyscall.so.1,但是后来因为他的新作用(作为用户空间和内核空间的一扇门)而改变了.虚拟系统调用并不是在每个平台上都需要,但是他们对于x86平台来说却是一个很精巧的设计.
传统的x86的系统调用使用中断完成.你可能还记得在很久以前的ms-dos系统上使用33(21h)中断作为系统调用.windows系统调用被隐藏在用户态API的下层,但是有时也会使用int 0x2e陷入.相似的,linux和其他*nix内核也使用中断(int 0x80)实现系统调用.
但是,事实证明,通过中断方式的系统调用在新的x86处理器上表现的很差,速度很慢.一个int 0x80系统调用在Pentium 4 2.0 上比Pentium III还要慢.对于很频繁使用系统调用的程序而言,对性能影响还是很大的.
[译者注:下面一小段是一些历史,不感兴趣可以跳过]
Intel已经发现了这个问题,并且引入一个更有效的系统调用方式:使用sysenter和sysexit指令.这种快速系统调用首先在 Pentium Pro 处理器上使用,但是因为硬件bug,直到后来的Pentium II,甚至Pentium III.硬件问题也帮助结识了为什么操作系统用了很长时间才开始支持快速系统调用.如果我们不算早期的实验型patch,linux支持sysenter 首先是在开发2.5内核的时候(2002年12月,这已经是指令定义的十年后了).MS开始支持sysenter稍微早一点,是在win XP中支持的.
如果你想检查一下自己的linux机器是否在使用sysenter指令进行系统调用,可以反汇编__kernel_vsyscall:
- objdump -d --start-address=0xffffe400 --stop-address=0xffffe414 linux-gate.dso
- linux-gate.dso: file format elf32-i386
- Disassembly of section .text:
- 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
- ffffe407: 90 nop
- ffffe408: 90 nop
- ffffe409: 90 nop
- ffffe40a: 90 nop
- ffffe40b: 90 nop
- ffffe40c: 90 nop
- ffffe40d: 90 nop
- ffffe40e: eb f3 jmp ffffe403 <__kernel_vsyscall+0x3>
- ffffe410: 5d pop %ebp
- ffffe411: 5a pop %edx
- ffffe412: 59 pop %ecx
- ffffe413: c3 ret
复制代码
系统使用哪一种方式完成系统调用在内核启动时决定.很明显,这台机子使用了sysenter.在老一点的机子上,你可能看到的就是int $0x80了.如果想搞明白那个jump是怎么回事,可以参考linus Torvalds的这篇文章:http://lkml.org/lkml/2002/12/18/218
原文链接:
http://www.linuxsir.org/bbs/showthread.php?t=216758
[ 本帖最后由 albcamus 于 2006-5-26 10:27 编辑 ] |
|