linux vdso与vsyscall相关内容总结

本文探讨了64位Linux环境下系统调用的过程,重点分析了vdso和vsyscall的作用及其安全性改善措施。通过实际案例展示了这两种机制如何提高系统效率并减少安全风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇blog里,简单分析了64位linux下系统调用的执行过程,其中还剩下两点内容:vdso与vsyscall。vdso的全称是虚拟动态共享库(virtual dynamic shared library),而vsyscall的全称是虚拟系统调用(virtual system call)。关于 vdso与vsyscall的介绍,请见以下这篇blog,内容上这篇blog分析的非常好,并且是以x86-64位的linux最为实例进行讲解,但这个系列blog是直接从源码出发,我现在分析内核或glibc的思路是从现象出发,层层深入,所以在思路上有些冲突。

https://0xax.gitbooks.io/linux-insides/content/SysCall/syscall-3.html

 在推荐另一篇blog:http://www.lenky.info/archives/2013/02/2199

同样是对64位linux的系统调用机制进行研究,但其中的某些实验我没有能够复现。

好,言归正传。

先来看vdso与vsyscall的出现原因:由于进行系统调用时,操作系统要由用户态切换到内核态,而这一操作是非常浪费时间的操作,无论采用早期的int 0x80/iret中断,还是sysenter/sysexit指令,再到syscall/sysexit指令。另一方面,某些系统调用并不会向内核提交参数,而仅仅只是从内核里请求读取某个数据,例如gettimeofday(),内核在处理这部分系统调用时可以把系统当前时间写在一个固定的位置,而应用程序直接从该位置简单读取即可,无需发起系统调用。内核与用户态程序之间进行数据交互的方法就是mmap。但由于vsyscall采用固定地址映射的方式,所以存在一定的安全隐患,这一方式便被vdso所改进,vdso的随机映射在一定程度上缓解了安全威胁。虽然有了vdso,但从历史兼容性上来讲,vsyscall不能就此完全抛弃,否则将导致一些陈旧的(特别是静态连接的)应用程序无法执行,因此现在在我的3.19内核上,将同时看到vdso和vsyscal。

举个简单的例子:

cat /proc/self/maps
00400000-0040c000 r-xp 00000000 08:08 2883607                            /bin/cat
0060b000-0060c000 r--p 0000b000 08:08 2883607                            /bin/cat
0060c000-0060d000 rw-p 0000c000 08:08 2883607                            /bin/cat
01778000-01799000 rw-p 00000000 00:00 0                                  [heap]
7eff551eb000-7eff558cd000 r--p 00000000 08:08 401027                     /usr/lib/locale/locale-archive
7eff558cd000-7eff55a8d000 r-xp 00000000 08:08 3412306                    /lib/x86_64-linux-gnu/libc-2.21.so
7eff55a8d000-7eff55c8d000 ---p 001c0000 08:08 3412306                    /lib/x86_64-linux-gnu/libc-2.21.so
7eff55c8d000-7eff55c91000 r--p 001c0000 08:08 3412306                    /lib/x86_64-linux-gnu/libc-2.21.so
7eff55c91000-7eff55c93000 rw-p 001c4000 08:08 3412306                    /lib/x86_64-linux-gnu/libc-2.21.so
7eff55c93000-7eff55c97000 rw-p 00000000 00:00 0 
7eff55c97000-7eff55cbb000 r-xp 00000000 08:08 3412278                    /lib/x86_64-linux-gnu/ld-2.21.so
7eff55e78000-7eff55e9d000 rw-p 00000000 00:00 0 
7eff55eb8000-7eff55eba000 rw-p 00000000 00:00 0 
7eff55eba000-7eff55ebb000 r--p 00023000 08:08 3412278                    /lib/x86_64-linux-gnu/ld-2.21.so
7eff55ebb000-7eff55ebc000 rw-p 00024000 08:08 3412278                    /lib/x86_64-linux-gnu/ld-2.21.so
7eff55ebc000-7eff55ebd000 rw-p 00000000 00:00 0 
7ffc08293000-7ffc082b4000 rw-p 00000000 00:00 0                          [stack]
7ffc082e4000-7ffc082e6000 r--p 00000000 00:00 0                          [vvar]
7ffc082e6000-7ffc082e8000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
此处可以清晰的看到在进程空间里同时存在vdso与vsyscall,我曾试着将这两个文件转储出来,不过还没有成功。

所以我也就没有办法从现象出发对源码进行分析。

此处还要提一个函数名为__kernel_vsyscall,名字和vsyscall很像,不过正好相反,这个函数是与vdso相关的函数。根据《深入理解linux内核》,__kernel_vsyscall是为兼容int 0x80与sysenter两种系统调用触发方式,而存在的函数。但在x64上不再需要__kernel_vsyscall,因为它只有一种调用方式,即syscall指令。


### 作用 Linux VDSO(Virtual Dynamic Shared Object)是一种特殊的共享库,其主要目的是提高某些频繁使用的系统调用的性能。VDSO将一些系统调用的实现从内核空间“映射”到用户空间,从而避免了传统系统调用所需的上下文切换开销。这种优化对于那些需要频繁调用且执行时间较短的系统调用尤为重要,例如`gettimeofday()`、`clock_gettime()`等。 在内核加载一个ELF可执行程序时,内核会自动在其进程地址空间中建立一个名为VDSO的内存区域。该区域通常以`linux-vdso.so.X`的形式出现在`/proc/self/maps`中,并且被映射到每个进程的地址空间中。这样,用户空间程序可以直接调用VDSO中的函数,而无需显式加载和链接该库[^4]。 ### 使用方法 在实际使用中,用户程序并不需要显式地加载或链接VDSO库。相反,操作系统会在程序启动时自动将其映射到进程的地址空间中。例如,在Linux 2.6及以上版本中,运行`ldd /bin/sh`会发现一个名为`linux-vdso.so.1`(旧版本可能为`linux-gate.so.1`)的动态链接库,尽管该文件在文件系统中并不存在。这是因为VDSO是由内核动态生成并映射到进程地址空间中的。 当有系统调用触发时,程序会调用VDSO页面中的`__kernel_vsyscall`处的汇编指令,进而通过`sysenter`或`syscall`指令进入内核态执行系统调用。这种方式减少了系统调用的开销,因为不需要通过传统的中断机制来切换到内核模式[^2]。 ### 相关问题 尽管VDSO带来了性能上的提升,但在某些情况下也可能引发一些问题: 1. **地址空间随机化**:由于VDSO在每个进程中的映射地址是随机的,这可能会导致某些依赖固定地址的调试工具或安全机制受到影响。例如,某些逆向工程工具可能需要处理VDSO地址的不确定性。 2. **兼容性问题**:不同架构和内核版本对VDSO的支持可能存在差异。例如,某些旧版本的Linux内核可能没有实现VDSO,或者提供的VDSO功能有限。这可能导致应用程序在不同环境中表现不一致。 3. **安全性**:虽然VDSO的设计本身是安全的,但其存在可能为攻击者提供了一种绕过某些安全机制的方式。例如,某些漏洞利用可能尝试通过VDSO中的函数来提升权限或绕过地址空间布局随机化(ASLR)等保护措施。 4. **调试困难**:由于VDSO是由内核直接映射到用户空间的,传统的调试工具(如`gdb`)可能无法直接访问或调试VDSO中的代码。这可能会增加调试VDSO相关系统调用问题的难度。 ### 示例代码 以下是一个简单的示例,展示了如何通过`gettimeofday()`函数间接使用VDSO中的功能。此函数通常会被优化为通过VDSO直接调用,而不是通过传统的系统调用路径。 ```c #include <sys/time.h> #include <stdio.h> int main() { struct timeval tv; int result = gettimeofday(&tv, NULL); if (result == 0) { printf("Current time: %ld seconds, %ld microseconds\n", tv.tv_sec, tv.tv_usec); } else { perror("gettimeofday"); } return 0; } ``` ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值