如何利用自己的编程语言利用VDSO对象?

时间:2021-03-23 23:11:16

Recent Linux kernels (at least on amd64) provide a magic object file called linux-vdso.so.1 that abstracts away the syscall interface to the kernel, allowing the kernel to choose the optimal calling convention. If you write code in C, the glibc automatically uses this object.

最近的Linux内核(至少在amd64上)提供了一个名为linux-vdso.so.1的魔术对象文件,它将syscall接口抽象到内核,允许内核选择最佳的调用约定。如果用C编写代码,glibc会自动使用该对象。

Now, if I want to write a program without using the glibc, how can I use this object? Is the interface it provides documented somewhere? What about the calling convention?

现在,如果我想在不使用glibc的情况下编写程序,我该如何使用这个对象?它提供的界面是否记录在某处?召唤大会怎么样?

3 个解决方案

#1


6  

It depends if your implementation is using C interface for low level utilities or not.

这取决于您的实现是否为低级实用程序使用C接口。

If your language gives direct access to syscalls without going thru the C wrapper you don't need to use VDSO (you could for instance generate the appropriate SYSENTER machine instruction to do the syscall). In that case, your language don't even need to follow the all the ABI conventions, just the conventions of the kernel. (for instance, you don't need the ABI provided caller-safe calle-safe distinguo on registers, and you could even avoid using any stacks).

如果您的语言直接访问系统调用而不通过C包装器,则不需要使用VDSO(例如,您可以生成相应的SYSENTER机器指令来执行系统调用)。在这种情况下,您的语言甚至不需要遵循所有ABI约定,只需遵循内核的约定。 (例如,您不需要ABI在寄存器上提供调用者安全的calle-safe区分,甚至可以避免使用任何堆栈)。

My understanding of the VDSO is that it is an abstraction, provided by the kernel, to abstract away the various small differences (related to user-land -> kernel transitions) in implementing syscalls, between various families of x86 processors. If you have chosen a particular processor target, you don't need VDSO, and you can always avoid it.

我对VDSO的理解是它是一个抽象,由内核提供,用于抽象出各种x86处理器系列之间实现系统调用的各种小差异(与用户域 - >内核转换相关)。如果您选择了特定的处理器目标,则不需要VDSO,您可以随时避免使用它。

AFAIU, the VDSO is an ELF shared object, sitting (on my Debian/AMD64 with a recently compiled 3.8.3 kernel) in the segment ffffffffff600000-ffffffffff601000; check exactly with cat /proc/self/maps where it is). So you just need to understand the organization of ELF shared objects and retrieve the symbols from it. See this & that links. The VDSO uses the C conventions for calling documented in the x86-64 ABI specification.

AFAIU,VDSO是一个ELF共享对象,位于ffffffffff600000-ffffffffff601000段中(在我的Debian / AMD64上,最近编译的3.8.3内核);用cat / proc / self / maps确切地检查它。因此,您只需要了解ELF共享对象的组织并从中检索符号。看到这个和那个链接。 VDSO使用C约定来调用x86-64 ABI规范中记录的调用。

That is, if you extract from your process space the VDSO and write it on a disk file, the result is a well formed ELF shared object

也就是说,如果从进程空间中提取VDSO并将其写入磁盘文件,则结果是格式良好的ELF共享对象

ELF is a well documented format. And so is the x86-64 ABI conventions (which defines precisely the C calling conventions, and how exactly a process' image starts. See also execve(2)) man page, and of course the kernel documentation, so I don't understand what is your issue. I agree that understanding ELF takes time (I did that 10 years ago, but my memory is rusty). Read also the <elf.h> header file on your machine.

ELF是一种记录良好的格式。 x86-64 ABI约定也是如此(它精确定义了C调用约定,以及进程'图像的确切开始。另请参见execve(2))手册页,当然还有内核文档,所以我不明白你的问题是什么?我同意理解ELF需要时间(我10年前做过,但我的记忆生疏)。另请阅读计算机上的 头文件。

For instance; running (under zsh on 64 bits Debian x86-64)

例如;运行(在64位Debian x86-64上的zsh下运行)

 % file $(which sash)
 /bin/sash: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
      statically linked, for GNU/Linux 2.6.26,
      BuildID[sha1]=0x0347fcc08fba2f811f58af99f26111d0f579a9f6, stripped

 % ldd $(which sash)
 not a dynamic executable

  % sash
  Stand-alone shell (version 3.7)
  > ps |grep sash
  21635 pts/3    00:00:00 sash
  > cat /proc/21635/maps
  00400000-004da000 r-xp 00000000 08:01 4985590                            /bin/sash
  006da000-006dc000 rw-p 000da000 08:01 4985590                            /bin/sash
  006dc000-006e1000 rw-p 00000000 00:00 0 
  017e3000-01806000 rw-p 00000000 00:00 0                                  [heap]
  7fe4950e5000-7fe4950e7000 rw-p 00000000 00:00 0 
  7fff3f130000-7fff3f151000 rw-p 00000000 00:00 0                          [stack]
  7fff3f173000-7fff3f175000 r-xp 00000000 00:00 0                          [vdso]
  ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

See also this answer.

另见这个答案。

You probably want inside your runtime a minimal version of a dynamic linker able to simply parse the VDSO. You certainly want to understand the exact state in which a process is started, and in particular the role of auxv, the auxiliary vector (I really forget these details, but I remember that they are important). See e.g. this article

您可能希望在运行时内部使用能够简单地解析VDSO的动态链接器的最小版本。你当然想要了解一个过程开始的确切状态,特别是auxv的作用,辅助向量(我真的忘记了这些细节,但我记得它们很重要)。参见例如本文

Actually, starting reliably your runtime is probably harder than the VDSO issue.

实际上,可靠地启动运行时可能比VDSO问题更难。

You may also want to read the linux assembly howto which also explains some things (but more about x86 than x86-64)

您可能还想阅读linux程序集howto,它也解释了一些内容(但更多关于x86而不是x86-64)

BTW,the code of http://musl-libc.org/ (which is an alternative libc) is much easier to read and understand (and you'll learn easily how they do dynamic linking, pthreads, etc..)

顺便说一句,http://musl-libc.org/(这是一个替代的libc)的代码更容易阅读和理解(你将很容易地学习它们如何进行动态链接,pthreads等。)

#2


4  

On digging the internet i found this link

在挖掘互联网时我发现了这个链接

http://www.linuxjournal.com/content/creating-vdso-colonels-other-chicken

I think it answers your question

我认为它回答了你的问题

#3


4  

I found these files in the Linux kernel tree helpful:

我发现Linux内核树中的这些文件很有帮助:

The vDSO object is a virtual dynamic shared object that is always mapped into the address space of an amd64 process under linux. It can be used to implement quick system calls. To access the functions inside the vDSO object, you need to

vDSO对象是一个虚拟动态共享对象,始终映射到linux下amd64进程的地址空间。它可用于实现快速系统调用。要访问vDSO对象内的函数,您需要

  • locate the object
  • 找到对象

  • extract an address from the symbol table
  • 从符号表中提取地址

Both things can be done with the CC0 licensed reference implementation parse_vdso.c.

这两件事都可以使用CC0许可的参考实现parse_vdso.c来完成。

#1


6  

It depends if your implementation is using C interface for low level utilities or not.

这取决于您的实现是否为低级实用程序使用C接口。

If your language gives direct access to syscalls without going thru the C wrapper you don't need to use VDSO (you could for instance generate the appropriate SYSENTER machine instruction to do the syscall). In that case, your language don't even need to follow the all the ABI conventions, just the conventions of the kernel. (for instance, you don't need the ABI provided caller-safe calle-safe distinguo on registers, and you could even avoid using any stacks).

如果您的语言直接访问系统调用而不通过C包装器,则不需要使用VDSO(例如,您可以生成相应的SYSENTER机器指令来执行系统调用)。在这种情况下,您的语言甚至不需要遵循所有ABI约定,只需遵循内核的约定。 (例如,您不需要ABI在寄存器上提供调用者安全的calle-safe区分,甚至可以避免使用任何堆栈)。

My understanding of the VDSO is that it is an abstraction, provided by the kernel, to abstract away the various small differences (related to user-land -> kernel transitions) in implementing syscalls, between various families of x86 processors. If you have chosen a particular processor target, you don't need VDSO, and you can always avoid it.

我对VDSO的理解是它是一个抽象,由内核提供,用于抽象出各种x86处理器系列之间实现系统调用的各种小差异(与用户域 - >内核转换相关)。如果您选择了特定的处理器目标,则不需要VDSO,您可以随时避免使用它。

AFAIU, the VDSO is an ELF shared object, sitting (on my Debian/AMD64 with a recently compiled 3.8.3 kernel) in the segment ffffffffff600000-ffffffffff601000; check exactly with cat /proc/self/maps where it is). So you just need to understand the organization of ELF shared objects and retrieve the symbols from it. See this & that links. The VDSO uses the C conventions for calling documented in the x86-64 ABI specification.

AFAIU,VDSO是一个ELF共享对象,位于ffffffffff600000-ffffffffff601000段中(在我的Debian / AMD64上,最近编译的3.8.3内核);用cat / proc / self / maps确切地检查它。因此,您只需要了解ELF共享对象的组织并从中检索符号。看到这个和那个链接。 VDSO使用C约定来调用x86-64 ABI规范中记录的调用。

That is, if you extract from your process space the VDSO and write it on a disk file, the result is a well formed ELF shared object

也就是说,如果从进程空间中提取VDSO并将其写入磁盘文件,则结果是格式良好的ELF共享对象

ELF is a well documented format. And so is the x86-64 ABI conventions (which defines precisely the C calling conventions, and how exactly a process' image starts. See also execve(2)) man page, and of course the kernel documentation, so I don't understand what is your issue. I agree that understanding ELF takes time (I did that 10 years ago, but my memory is rusty). Read also the <elf.h> header file on your machine.

ELF是一种记录良好的格式。 x86-64 ABI约定也是如此(它精确定义了C调用约定,以及进程'图像的确切开始。另请参见execve(2))手册页,当然还有内核文档,所以我不明白你的问题是什么?我同意理解ELF需要时间(我10年前做过,但我的记忆生疏)。另请阅读计算机上的 头文件。

For instance; running (under zsh on 64 bits Debian x86-64)

例如;运行(在64位Debian x86-64上的zsh下运行)

 % file $(which sash)
 /bin/sash: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
      statically linked, for GNU/Linux 2.6.26,
      BuildID[sha1]=0x0347fcc08fba2f811f58af99f26111d0f579a9f6, stripped

 % ldd $(which sash)
 not a dynamic executable

  % sash
  Stand-alone shell (version 3.7)
  > ps |grep sash
  21635 pts/3    00:00:00 sash
  > cat /proc/21635/maps
  00400000-004da000 r-xp 00000000 08:01 4985590                            /bin/sash
  006da000-006dc000 rw-p 000da000 08:01 4985590                            /bin/sash
  006dc000-006e1000 rw-p 00000000 00:00 0 
  017e3000-01806000 rw-p 00000000 00:00 0                                  [heap]
  7fe4950e5000-7fe4950e7000 rw-p 00000000 00:00 0 
  7fff3f130000-7fff3f151000 rw-p 00000000 00:00 0                          [stack]
  7fff3f173000-7fff3f175000 r-xp 00000000 00:00 0                          [vdso]
  ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

See also this answer.

另见这个答案。

You probably want inside your runtime a minimal version of a dynamic linker able to simply parse the VDSO. You certainly want to understand the exact state in which a process is started, and in particular the role of auxv, the auxiliary vector (I really forget these details, but I remember that they are important). See e.g. this article

您可能希望在运行时内部使用能够简单地解析VDSO的动态链接器的最小版本。你当然想要了解一个过程开始的确切状态,特别是auxv的作用,辅助向量(我真的忘记了这些细节,但我记得它们很重要)。参见例如本文

Actually, starting reliably your runtime is probably harder than the VDSO issue.

实际上,可靠地启动运行时可能比VDSO问题更难。

You may also want to read the linux assembly howto which also explains some things (but more about x86 than x86-64)

您可能还想阅读linux程序集howto,它也解释了一些内容(但更多关于x86而不是x86-64)

BTW,the code of http://musl-libc.org/ (which is an alternative libc) is much easier to read and understand (and you'll learn easily how they do dynamic linking, pthreads, etc..)

顺便说一句,http://musl-libc.org/(这是一个替代的libc)的代码更容易阅读和理解(你将很容易地学习它们如何进行动态链接,pthreads等。)

#2


4  

On digging the internet i found this link

在挖掘互联网时我发现了这个链接

http://www.linuxjournal.com/content/creating-vdso-colonels-other-chicken

I think it answers your question

我认为它回答了你的问题

#3


4  

I found these files in the Linux kernel tree helpful:

我发现Linux内核树中的这些文件很有帮助:

The vDSO object is a virtual dynamic shared object that is always mapped into the address space of an amd64 process under linux. It can be used to implement quick system calls. To access the functions inside the vDSO object, you need to

vDSO对象是一个虚拟动态共享对象,始终映射到linux下amd64进程的地址空间。它可用于实现快速系统调用。要访问vDSO对象内的函数,您需要

  • locate the object
  • 找到对象

  • extract an address from the symbol table
  • 从符号表中提取地址

Both things can be done with the CC0 licensed reference implementation parse_vdso.c.

这两件事都可以使用CC0许可的参考实现parse_vdso.c来完成。