- 本文写于2021年7月,可能官方文档会继续更新,因此不能保证在将来本文依然对大家有帮助。
- 英文原文使用了非常长的定语从句,由于能力有限,尽量拆分为小段句子,并尽量能让中文读起来顺畅。
- 尽量保留专业术语&词汇,以免引起二义性。
- 由于目前对 Fuchsia 和 Zircon 不熟悉,难免有翻译失误的地方,欢迎斧正。
- 一边翻译一边学习,暂时不能理解的地方留了 TODO,争取以后补上。对暂时不知如何翻译的单词会用【 xxx 】做翻译参考。
- 上班996,下班有空做翻译,不能保证更新速度。
- 谢绝转载
- 谢绝转载
- 谢绝转载
Zircon vDSO
The Zircon vDSO is the sole means of access to system calls in Zircon. vDSO stands for virtual Dynamic Shared Object. (Dynamic Shared Object is a term used for a shared library in the ELF format.) It's virtual because it's not loaded from an ELF file that sits in a filesystem. Instead, the vDSO image is provided directly by the kernel.
Zircon vDSO 是访问 system call 的唯一方法。 vDSO 代表 virtual Dynamic Shared Object。( Dynamic Shared Object 是ELF格式共享库的术语。)由于它不会从EFL文件中载入并出现在文件系统中,因此称之为虚拟的。相反,vDSO image 是由 kernel 直接提供。
使用 vDSO | Using the vDSO
ABI 系统调用 | System Call ABI
The vDSO is a shared library in the ELF format. It's used in the normal way that ELF shared libraries are used, which is to look up entry points by symbol name in the ELF dynamic symbol table (the .dynsym section, located via DT_SYMTAB). ELF defines a hash table format to optimize lookup by name in the symbol table (the .hash section, located via DT_HASH); GNU tools have defined an improved hash table format that makes lookups much more efficient (the .gnu_hash section, located via DT_GNU_HASH). Fuchsia ELF shared libraries, including the vDSO, use the DT_GNU_HASH format exclusively. (It's also possible to use the symbol table directly via linear search, ignoring the hash table.)
The vDSO uses a simplified layout that has no writable segment and requires no dynamic relocations. This makes it easier to use the system call ABI without implementing a general-purpose ELF loader and full ELF dynamic linking semantics.
ELF symbol names are the same as C identifiers with external linkage. Each system call corresponds to an ELF symbol in the vDSO, and has the ABI of a C function. The vDSO functions use only the basic machine-specific C calling conventions governing the use of machine registers and the stack, which is common across many systems that use ELF, such as Linux and all the BSD variants. They do not rely on complex features such as ELF Thread-Local Storage, nor on Fuchsia-specific ABI elements such as the SafeStack unsafe stack pointer. To see more information about the life of a syscall and its relationship to the vDSO, see Life of a Fuchsia syscall.
vDSO 是一个ELF格式的共享库。它按ELF共享库的使用方式被使用,即在EFL中的 dynamic symbol 表中(位于 .dynsym 段, DT_SYMTAB 标识),用 symbol 名称找到 entry point。ELF定义了一个哈希表格式来优化 symbol 表中的名字查找(位于 .hash 段, DT_HASH 标识);GNU 工具定义了一个改进的 hash 表格式使得查找变得更加高效(位于 .gnu_hash 段, DT_GNU_HASH 标识)。Fuchsia EFL 共享库,包含 vDSO,仅使用 DT_GNU_HASH 格式。(也可以不适用哈希表,而通过 symbol 表直接进行线性查找。)
vDSO 使用 simplified layout 【简化布局】,没有可写区,也并不需要 dynamic relocation 【动态重定位】。这样能更方便的使用 system call ABI,且不需要实现通用目的的 ELF loader 和 全部 ELF dynamic linking semantics 【动态链接语法】。Vx公众号:MochaCode
ELF 符号名与具有外部链接的 C 标识符一样。每个 system call 都对应一个 vDSO 中的 ELF 符号,并具有 C 函数的 ABI。vDSO 函数只使用基本的、设备相关的 C 调用约定,来控制设备寄存器和栈的使用,这种方式对许多使用 ELF 的系统来说很常见,比如 Linux 以及所有 BSD 变体版本。他们不依赖于复杂的功能,比如 ELF Thread-Local Storage【线程本地存储】或 基于 Fuchsia 的 ABI 元素(比如 SafeStack unsafe stack 指针)。如需获取更多关于 syscall 生命周期和与 VDSO 关系的信息,请参阅 《 Fuchsia syscall 生命周期》。
vDSO Unwind Information
The vDSO has an ELF program header of type PT_GNU_EH_FRAME. This points to unwind information in the GNU .eh_frame format, which is a close relative of the standard DWARF Call Frame Information format. This information makes it possible to recover the register values from call frames in the vDSO code, so that a complete stack trace can be reconstructed from any thread's register state with a PC value inside the vDSO code. These formats and their use are just the same in the vDSO as they are in any normal ELF shared library on Fuchsia or other systems using common GNU ELF extensions, such as Linux and all the BSD variants.
vDSO 拥有 ELF 程序的 PT_GNU_EH_FRAME 类型头。它指向了GNU .eh_frame 格式的 unwind 信息,与标准 DWARF Call Frame Information 格式类似。这些信息使它可以从 vDSO 代码的 call frame 中恢复寄存器的值,这样一来,使用vDSO代码中的 PC 值,就可以从任意 thread 的寄存器状态重建一个完整的 stack trace 。这些格式和它们在 vDSO 中的用法,与任意普通 ELF 共享库,这些普通共享库被广泛用于 Fuchsia 或其他使用普通 GNU ELF 扩展的系统上(例如 Linux 和 所有 BSD 变体版本)。
vDSO Build ID
The vDSO has an ELF Build ID, as other ELF shared libraries and executables built with common GNU extensions do. The Build ID is a unique bit string that identifies a specific build of that binary. This is stored in ELF note format, pointed to by an ELF program header of type PT_NOTE. The payload of the note with name "GNU" and type NT_GNU_BUILD_ID is a sequence of bytes that constitutes the Build ID.
One main use of Build IDs is to associate binaries with their debugging information and the source code they were built from. The vDSO binary is innately tied to (and embedded within) the kernel binary and includes information specific to each kernel build, so the Build ID of the vDSO distinguishes kernels as well.
vDSO 拥有一个 ELF Build ID,就像其他 ELF 共享库和通过普通 GNU 扩展生成的可执行程序一样。 Build ID 是一串唯一的字符串,用以标识特定版本的二进制文件。它被存储为 ELF note 格式,并使用 ELF 程序头的 PT_NOTE 类型指向。名为 “GNU” 以及 NT_GNU_BUILD_ID 类型的 note 的负载数据,是一串构成 Build ID 的字节序列。
Build ID 的一个主要用途是,将二进制文件与它们的调试信息和生成它们的源代码联系起来。vDSO 二进制耳机天生就与 kernel 二进制文件联系在一起(并嵌入其中),并包含了每个 kernel 编译的特定信息,因此 vDSO 的 Build ID 也可以用来区分 kernel 版本。
zx_process_start() argument
The zx_process_start() system call is how a program loader tells the kernel to start a new process's first thread executing. The final argument (arg2 in the zx_process_start() documentation) is a plain uintptr_t value passed to the new thread in a register.
By convention, the program loader maps the vDSO into each new process's address space (at a random location chosen by the system) and passes the base address of the image to the new process's first thread in the arg2 register. This address is where the ELF file header can be found in memory, pointing to all the other ELF format elements necessary to look up symbol names and thus make system calls.
zx_process_start 系统调用是 program loader 【程序装载器】告诉 kernel 如何启动一个新 process 的第一个 thread 的运行。最后一个参数(文档中的 arg2)是一个普通的 uintprt_t 值,通过寄存器传入到新 thread。
按照惯例,program loader 【程序装载器】将 vDSO 映射到每个新 process 的地址空间中(由系统选择的随机位置),并且将 image 的 base address 【起始地址】通过 arg2 寄存器,传递给新 process 的第一个 thread。ELF 文件的头信息在这个内存地址中可以被找到,头信息指向所有其他 ELF 格式元素,这些元素可以用来查找 symbol 名称,然后进行系统调用。
// zx_status_t zx_process_start
zx_status_t sys_process_start(zx_handle_t process_handle, zx_handle_t thread_handle,
zx_vaddr_t pc, zx_vaddr_t sp,
zx_handle_t arg_handle_value, uintptr_t arg2)
PA_VMO_VDSO handle
The vDSO image is embedded in the kernel at compile time. The kernel exposes it to userspace as a read-only VMO.
When a program loader sets up a new process, the only way to make it possible for that process to make system calls is for the program loader to map the vDSO into the new process's address space before its first thread starts running. Hence, each process that will launch other processes capable of making system calls must have access to the vDSO VMO.
By convention, a VMO handle for the vDSO is passed from process to process in the zx_proc_args_t bootstrap message sent to each new process (see <zircon/processargs.h>). The VMO handle's entry in the handle table is identified by the handle info entry PA_HND(PA_VMO_VDSO, 0).
vDSO image 在编译时被嵌入到 kernel 中。kernel 将其以只读 VMO 形式暴露给 userspace。
当 program loader 【程序装载器】创建好一个新的 process, 在该 process 的第一个thread 运行前,仅有一种方式能让它进行系统调用:让program loader 将 vDSO 映射到新 process 的地址空间中。因此每个 process 必须能够访问 vDSO VMO,这样才能使得它将来启动的其它 process 能够进行系统调用。Vx公众号:MochaCode
按照惯例,引用 vDSO 的 VMO handle ,通过 zx_proc_args_t 启动消息在 process 之间传递(参阅:zircon/processargs.h)。 在 handle 表中的 VMO handle 的入口被标识为 handle info entry PA_HND(PA_VMO_VDSO, 0).
// handle is duplicable, transferable, readable, and executable, but not
// writable. The contents of the VM object should be treated like any
// other general-purpose ELF file image of type ET_DYN. A process only
// needs this handle so that it can map the vDSO into new processes it
// might create or propagate it on to its children so they can do so.
// Each process's own vDSO was mapped in by its creator before the
// process started, its address passed as an argument to entry point.
#define PA_VMO_VDSO 0x11u
A Virtual Memory Object (VMO) represents a contiguous region of virtual memory
that may be mapped into multiple address spaces.
// Handle Info entries associate a type and optional
// argument with each handle included in the process
// arguments message.
#define PA_HND(type, arg) (((type)&0xFF) | (((arg)&0xFFFF) << 16))
vDSO 实现细节 | vDSO Implementation Details
Kazoo 工具 | kazoo tool
The kazoo tool generates both C/C++ function declarations that form the public system call API, and some C++ and assembly code used in the implementation of the vDSO. Both the public API and the private interface between the kernel and the vDSO code are specified by the .fidl files in //zircon/vdso.
The syscalls fall into the following groups, distinguished by the presence of attributes after the system call name:
- Entries with neither vdsocall nor internal are the simple cases (which are the majority of the system calls) where the public API and the private API are exactly the same. These are implemented entirely by generated code. The public API functions have names prefixed by zx and zx_ (aliases).
- vdsocall entries are simply declarations for the public API. These functions are implemented by normal, hand-written C++ code found in the kernel source. Those source files #include "private.h" and then define the C++ function for the system call with its name prefixed by zx. Finally, they use the VDSO_INTERFACE_FUNCTION macro on the system call's name prefixed by zx_ (no leading underscore). This implementation code can call the C++ function for any other system call entry (whether a public generated call, a public hand-written vdsocall, or an internal generated call), but must use its private entry point alias, which has the VDSO_zx_ prefix. Otherwise the code is normal (minimal) C++, but must be stateless and reentrant (use only its stack and registers).
- internal entries are declarations of a private API used only by the vDSO implementation code to enter the kernel (i.e., by other functions implementing vdsocall system calls). These produce functions in the vDSO implementation with the same C signature that would be declared in the public API given the signature of the system call entry. However, instead of being named with the zx and zx_ prefixes, these are only available through #include "private.h" with VDSO_zx_ prefixes.
kazoo 工具生成:构成公共系统调用 API 的 C/C++ 函数声明,以及一些实现 vDSO 的 C++ 和 汇编代码。kernel 和 vDSO 代码之间的公用 API 和私有接口由 .fidl 文件指定(zircon/vdso)。
通过 syscall 函数名后的 attribute 【属性】来区分,syscall 可以分为以下几类:
- 最简单的情况是 entry【函数】既没有 vdso call 也没有内部调用(绝大多数 system call 都是如此),其公共和私有 API 是完全一致的。它们完全由生成的代码实现。公共 API 的函数名有前缀 zx 或 zx_ (别名)。
- Vdsocall entry 【函数】只是公共 API 的简单声明。这些函数是由普通手写的 C++ 代码实现,它们在 kernel 源码中可以被找到。这些源码文件包含了 “private.h”, 然后为 system call 定义了 C++ 函数,其函数名都有前缀 zx。最后对 system call 函数名使用 VDSO_INTERFACE_FUNCTION 宏,使之名字以 zx_ 开头(无前导下横线 “”)。这样实现的代码能够为任意其他 system call entry 调用 C++ 函数(无论这些 system call 是公共生成的 call,或是公共手写的 vdsocall, 或是内部生成的 call),但必须使用他们的私有 entry point 的别名,这些别名都已 VDSO_zx 开头。否则代码就是普通的(最小) C++, 但是必须为无状态且可重入的(仅使用它的栈和寄存器)。Vx公众号:MochaCode
- 内部 entry 被声明为私有 API,仅由实现 vDSO 的代码使用来进入 kernel(例如,由实现 vDSO system call 的其他函数调用)。在 vDSO 实现代码中产生的函数,会被声明为公共 API ,函数和 API 都拥有相同的 system call entry C 签名。但是,它们不会以 zx 或 zx_ 为前缀,而是在 “private.h” 文件中以 VDSO_zx_ 为前缀而存在。
【注:这几段非常的绕,需要结合代码一起阅读,目前翻译估计有错 TODO】
// Code should define '_zx_foo' and then do 'VDSO_INTERFACE_FUNCTION(zx_foo);'
// to define the public name 'zx_foo' and the vDSO-private name 'VDSO_zx_foo'.
#define VDSO_INTERFACE_FUNCTION(name) \
decltype(name) name __WEAK_ALIAS("_" #name); \
decltype(name) VDSO_##name __LOCAL __ALIAS("_" #name)
只读动态共享对象布局 | Read-Only Dynamic Shared Object Layout
The vDSO is a normal ELF shared library and can be treated like any other. But it's intentionally kept to a small subset of what an ELF shared library in general is allowed to do. This has several benefits:
- Mapping the ELF image into a process is straightforward and does not involve any complex corner cases of general support for ELF PT_LOAD program headers. The vDSO's layout can be handled by special-case code with no loops that reads only a few values from ELF headers.
- Using the vDSO does not require full-featured ELF dynamic linking. In particular, the vDSO has no dynamic relocations. Mapping in the ELF PT_LOAD segments is the only setup that needs to be done.
- The vDSO code is stateless and reentrant. It refers only to the registers and stack with which it's called. This makes it usable in a wide variety of contexts with minimal constraints on how user code organizes itself, which is appropriate for the mandatory ABI of an operating system. It also makes the code easier to reason about and audit for robustness and security.
The layout is simply two consecutive segments, each containing aligned whole pages:
- The first segment is read-only, and includes the ELF headers and metadata for dynamic linking along with constant data private to the vDSO's implementation.
- The second segment is executable, containing the vDSO code.
The whole vDSO image consists of just these two segments' pages, present in the ELF image just as they should appear in memory. To map in the vDSO requires only two values gleaned from the vDSO's ELF headers: the number of pages in each segment.
vDSO 是一个普通的 ELF 共享库,可以与其他库一样对待。但 ELF 共享库通常允许所做的事情中,它特地仅保留了一小部分。这样做有几点好处:
- 将 ELF image 映射到 process 并不复杂,对 ELF PT_LOAD 程序头的一般支持也无需涉及任何复杂的极端情况。vDSO 布局可以用特例代码来处理,无需循环,仅从 ELF 头中读取少量值。
- 使用 vDSO 不要求全功能的 ELF 动态链接。尤其是 vDSO 没有动态重定向。映射 ELF PT_LOAD 段是唯一需要做的设置。
- vDSO 代码是无状态、可重入的。仅引用调用它的寄存器和栈。这使得它可以在各式各样的上下文中被使用,对用户如何组织自身代码的限制也非常小,这适合于操作系统的必有的 ABI。也使得代码更易于推理和检查健壮性、安全性。
布局是2个连续的段,每段包含了对齐的整页:
- 第1段是只读的,包含了 ELF 头和metadata,用来进行动态链接以及 vDSO 实现的内部固定数据。
- 第2段是可运行的,包含 vDSO 代码。
整个 vDSO image 仅由2段的页面组成,它们在 ELF image 和内存中的呈现一致。若需映射 vDSO ,仅需要从 vDSO ELF 头中获取2个值:每段中页的数量。
Boot时只读数据 | Boot-time Read-Only Data
Some system calls simply return values that are constant throughout the runtime of the whole system, though the ABI of the system is that their values must be queried at runtime and cannot be compiled into user code. These values either are fixed in the kernel at compile time or are determined by the kernel at boot time from hardware or boot parameters. Examples include zx_system_get_version_string(), zx_system_get_num_cpus(), and zx_ticks_per_second().
Because these values are constant, there is no need to pay the overhead of entering the kernel to read them. Instead, the vDSO implementations of these are simple C++ functions that just return constants read from the vDSO's read-only data segment. Values fixed at compile time (such as the system version string) are simply compiled into the vDSO.
For the values determined at boot time, the kernel must modify the contents of the vDSO. This is accomplished by the boot-time code that sets up the vDSO VMO, before it starts the first userspace process and gives it the VMO handle. At compile time, the offset into the vDSO image of the vdso_constants data structure is extracted from the vDSO ELF file that will be embedded in the kernel. At boot time, the kernel temporarily maps the pages of the VMO covering vdso_constants into its own address space long enough to initialize the structure with the right values for the current run of the system.
一些 system call 仅返回值,这些值在整个系统运行时都是常量,尽管系统 ABI 只能在运行时被查询,而不能被编译到代码中。这些值要么在 kernel 编译的时候就被固定了,要么是在 kernel boot 时由硬件或 boot 参数决定。示例包括 zx_system_get_version_string( ),zx_system_get_num_cpus( ), 和 zx_ticks_per_second( )。
由于这些值是常量,无需为其支付进入 kernel 内读取他们的开销。相反,vDSO 对这些值的读取是用简单 C++ 函数来实现,函数仅返回从 vDSO 只读数据段读取的常量。 那些在编译阶段就被固定下来的值(例如系统版本字串),被编译到了 vDSO 中。
对于那些在 boot 时才被决定的值,kernel 必须修改 vDSO 的内容。这是由设置 vDSO VMO 的 boot-time 代码,在启动第一个 userspace process 并且传递给它 VMO handle 之前完成。在编译阶段,vdos_contants 数据结构在 vDSO image 中的偏移量,该偏移量是从嵌入 kernel 的 vDSO ELF 文件中抽取的。在 boot 阶段,kernel 将 VMO 的页临时映射到它自己的地址空间以覆盖 vdso_contants,这个地址空间需要足够长,为当前运行的系统用正确的值来初始化结构体。
强制措施 | Enforcement
The vDSO entry points are the only means to enter the kernel for system calls. The machine-specific instructions used to enter the kernel (e.g. syscall on x86) are not part of the system ABI and it's invalid for user code to execute such instructions directly. The interface between the kernel and the vDSO code is a private implementation detail.
Because the vDSO is itself normal code that executes in userspace, the kernel must robustly handle all possible entries into kernel mode from userspace. However, potential kernel bugs can be mitigated somewhat by enforcing that each kernel entry be made only from the proper vDSO code. This enforcement also avoids developers of userspace code circumventing the ABI rules (because of ignorance, malice, or misguided intent to work around some perceived limitation of the official ABI), which could lead to the private kernel-vDSO interface becoming a de facto ABI for application code.
The kernel enforces correct use of the vDSO in two ways:
-
It constrains how the vDSO VMO can be mapped into a process. When a zx_vmar_map() call is made using the vDSO VMO and requesting ZX_VM_PERM_EXECUTE, the kernel requires that the offset and size of the mapping exactly match the vDSO's executable segment. It also allows only one such mapping. Once the valid vDSO mapping has been established in a process, it cannot be removed. Attempts to map the vDSO a second time into the same process, to unmap the vDSO code from a process, or to make an executable mapping of the vDSO that don't use the correct offset and size, fail with ZX_ERR_ACCESS_DENIED.
-
At compile time, the offset and size of the vDSO's code segment are extracted from the vDSO ELF file and used as constants in the kernel's mapping enforcement code.
When the one valid vDSO mapping is established in a process, the kernel records the address for that process so it can be checked quickly.
- It constrains what PC locations can be used to enter the kernel. When a user thread enters the kernel for a system call, a register indicates which low-level system call is being invoked. The low-level system calls are the private interface between the kernel and the vDSO; many correspond directly the system calls in the public ABI, but others do not. For each low-level system call, there is a fixed set of PC locations in the vDSO code that invoke that call. The source code for the vDSO defines internal symbols identifying each such location. At compile time, these locations are extracted from the vDSO's symbol table and used to generate kernel code that defines a PC validity predicate for each low-level system call. Since there is only one definition of the vDSO code used by all user processes in the system, these predicates simply check for known, valid, constant offsets from the beginning of the vDSO code segment. On entry to the kernel for a system call, the kernel examines the PC location of the syscall instruction on x86 (or equivalent instruction on other machines). It subtracts the base address of the vDSO code recorded for the process at zx_vmar_map() time from the PC, and passes the resulting offset to the validity predicate for the system call being invoked. If the predicate rules the PC invalid, the calling thread is not allowed to proceed with the system call and instead takes a synthetic exception similar to the machine exception that would result from invoking an undefined or privileged machine instruction.
vDSO 是 system call 进入 kernel 的唯一入口。用于进入 kernel 的基于设备的指令(例如 x86 上的 syscall)并不是 system ABI 的一部分,并且用户代码直接执行这些指令是无效的。 Kernel 和 vDSO 代码之间的接口是私有实现的细节。 【注:最后一句请看原文,翻译并不通顺 TODO】
由于 vDSO 自身是在 userspace 中执行的普通代码, kernel 必须鲁棒的处理从 userspace 进入 kernel 的所有可能入口。但是, 通过强制每个进入 kernel 的入口都只能由恰当的 vDSO 调用产生,可以一定程度上减少可能出现的 kernel bug。这种强制措施也能避免 userspace 代码的开发者绕过 ABI 规则(由于疏忽、恶意或者错误的意图来绕过 ABI 官方可感知的限制),导致私有 kernel- vDSO 接口成为了应用程序实际上的 ABI。Vx公众号:MochaCode
Kernel 通过2种途径强制 vDSO 使用的正确性:
-
限制 vDSO VMO 如何映射到 process 的方式
- 当 zx_vmar_map( ) 调用是由 vDSO VMO 产生并请求 ZX_VM_PERM_EXECUTE,kernel 要求映射的偏移量和大小必须严格匹配 vDSO 的可运行段。它还仅允许一次这样的映射。一旦有效的 vDSO 映射已经在 process 中被建立,它就不能被移除。尝试第二次映射 vDSO 到相同的 process,或取消 vDSO 代码的映射,或执行 vDSO 的可运行映射但未使用正确的偏移量和大小,这些执行都会返回 ZX_ERR_ACCESS_DENIED。
- 在编译阶段,vDSO 代码段的偏移量和大小会从 vDSO ELF 文件中提取出来,并在 kernel 的映射执行代码被用作常量。
- 当一个 process 中建立了有效的 vDSO 映射,kernel 会为 process 记录下地址,这样它能够被快速检查。
-
限制能进入 kernel 的 PC 位置
- 当一个 user thread 进入 kernel 进行 system call,一个寄存器标明了正在对哪个底层 system call 进行调用。底层 system call 是 kernel 和 vDSO 之间的私有接口;许多接口与公共 ABI 的 system call 直接对应,其余的则不是。
- 对于每个底层 system call ,在 vDSO 代码中都有一个固定的 PC 地址集来调用它。vDSO 的源码定义了内部符号来标识每个这样的地址。在编译阶段,这些地址从 vDSO 符号表中被抽取出来,并用于生成 kernel 代码,这些代码为每个底层 system call 定义了 PC 有效性断言。由于仅有一个 vDSO 代码的定义被用于系统中的所有 user process,这些声明只检查从 vDSO 代码段开头的已知的、有效的、固定的偏移量。
- 在进入 kernel 进行 system call 时,kernel 检查该 syscall 在 x86 指令集中的 PC 地址(或者其他设备上的等效指令集)。它减去记录在 process 中的 vDSO 代码中的基地址,该基地址是在执行 zx_vmar_map 时从 PC 获取的,并将减去后的偏移量结果传递给正在调用的 system call 的有效性断言。如果该断言判定 PC 无效,调用 thread 就不被允许处理该 system call, 并且调用 thread 会得到一个类似设备异常的 synthetic 异常,该设备异常可能是由调用未定义的或设备优先的指令返回的。
变体版本 | Variants
TODO(mcgrathr): vDSO variants are an experimental feature that is not yet in real use. There is a proof-of-concept implementation and simple tests, but more work is required to implement the concept robustly and determine what variants will be made available. The concept is to provide variants of the vDSO image that export only a subset of the full vDSO system call interface. For example, system calls intended only for use by device drivers might be elided from the vDSO variant used for normal application code.
TODO(mcgrathr): vDSO 变体版本是一个实验性质的功能,并未实际使用。这是一个验证概念的实现和简单测试,但是需要更多的工作来实现这个概念的鲁棒性,以及决定什么样的变体版本将来可用。该概念用于提供 vDSO image 变体版本,该变体版本仅导出全 vDSO system call 接口的一个子集。例如,用于普通应用代码的 vDSO 变体版本中,可能会省略仅被设备驱动使用那些的 system call。
喜欢的话可以关注微信公众号:摩卡Code (MochaCode)