void *vdso = (uintptr_t) getauxval(AT_SYSINFO_EHDR);
いったいなぜ vDSO は存在しているのか? カーネルが提供するシステムコールのいくつかは、 ユーザー空間のコードがこれらのシステムコールを頻繁に呼び出すことになり、 このような呼び出しが全体の性能を支配するようになる場合がある。 これは、 呼び出しの頻度と、 ユーザー空間から抜けてカーネルに入ることによるコンテキストスイッチのオーバーヘッドの両方に起因する。
この文書の残りの部分は、 一般の開発者向けというではなく、 好奇心がある人と C ライブラリの開発者向けの内容となっている。 もしあなたが C ライブラリではなく自分のアプリケーションで vDSO を呼びだそうとしているのであれば、 ほとんどの場合間違ったことをしていることだろう。
用語が紛らわしい点には注意が必要である。 x86 システムでは、 システムコールを呼び出す推奨される方法を判定するのに使用される vDSO 関数は "__kernel_vsyscall" という名前だが、 x86=64 では、 "vsyscall" という用語は、 カーネルに時刻はいつかや呼び出し元はどの CPU 上にいるかを問い合わせるための廃止予定の方法も参照している。
頻繁に使用されるシステムコールの一つが gettimeofday(2) である。 このシステムコールは、 ユーザー空間アプリケーションから直接呼び出されることも、 C ライブラリから間接的に呼び出されることもある。 タイムスタンプが必要な場面、 タイミングループを行う場面、 ポーリングを行う場面を考えてほしい。 これらはいずれも現在時刻が何かを直ちに知りたいのが普通である。 また、この情報は秘密ではなく、 (ルートでも非特権ユーザーでも) 任意の特権モードの多くのアプリケーションが同じ情報を取得できる。 したがって、 カーネルはこの質問に応えるのに必要な情報をプロセスがアクセスできるメモリー上に配置する。 これにより、 gettimeofday(2) はシステムコールから通常の関数コールになり、 少ないメモリーアクセスになる。
vDSO がユーザーのメモリーマップの何か特定の場所にマッピングされると仮定してはならない。 通常新しいプロセスイメージが作成されるたびに (execve(2) 実行時点) 、 実行時にベースアドレスのランダム化が行われる。 これは "return-to-libc" 攻撃 を防ぐためにセキュリティ上の理由から行われる。
アーキテクチャーによっては AT_SYSINFO タグもある。 このタグは vsyscall エントリーポイントの場所を知るためだけのものであり、 しばしば省略されるか (利用できない場合は) 0 にセットされる。 このタグは最初の vDSO の実装で使用されていたものであり (下記の「歴史」を参照)、 このタグを利用するのは避けるべきである。
すべてのシンボルは (GNU のバージョンフォーマットを使って) バージョンが付けられている。 これにより、 カーネルは後方互換性を持たせつつ関数のシグネチャーを更新することができる。 つまり、 関数が受け取る引数や返り値が変更されることがあるということである。 したがって、 vDSO のシンボルを検索する際には、 自分が期待する ABI に一致するバージョンをしなければならない。
通常は vDSO はすべてのシンボルに "__vdso_" か "__kernel_" というプレフィックスを付けるという慣習に従った名前付けを行っており、 他の標準のシンボルから区別することができる。 例えば、 "gettimeofday" 関数は ""__vdso_gettimeofday" という名前になっている。
これらの関数を呼び出す場合は標準の C の呼び出しの慣習にしたがっておけばよい。 特殊なレジスターやスタックの動作に気を使う必要はない。
find arch/$ARCH/ -name '*vdso*.so*' -o -name '*gate*.so*'
ユーザー ABI | vDSO 名 |
aarch64 | linux-vdso.so.1 |
arm | linux-vdso.so.1 |
ia64 | linux-gate.so.1 |
mips | linux-vdso.so.1 |
ppc/32 | linux-vdso32.so.1 |
ppc/64 | linux-vdso64.so.1 |
riscv | linux-vdso.so.1 |
s390 | linux-vdso32.so.1 |
s390x | linux-vdso64.so.1 |
sh | linux-gate.so.1 |
i386 | linux-gate.so.1 |
x86-64 | linux-vdso.so.1 |
x86/x32 | linux-vdso.so.1 |
使用される vDSO は、 カーネルの ABI ではなく、 ユーザー空間コードの ABI に基づくことに注意すること。 したがって、 例えば、 i386 32 ビットの ELF ライブラリ上で実行する場合、 i386 32 ビットカーネル上で実行されているか x86-64 64 ビットカーネル上で実行されているかに関わらず同じ vDSO が得られる。 したがって、 以下のどの節が関係するかを判断する際にはユーザー空間 ABI の名前を使用する必要がある。
シンボル | バージョン |
__vdso_gettimeofday | LINUX_2.6 (Linux 4.1 以降で公開) |
__vdso_clock_gettime | LINUX_2.6 (Linux 4.1 以降で公開) |
Additionally, the ARM port has a code page full of utility functions. Since it's just a raw page of code, there is no ELF information for doing symbol lookups or versioning. It does provide support for different versions though.
For information on this code page, it's best to refer to the kernel documentation as it's extremely detailed and covers everything you need to know: Documentation/arm/kernel_user_helpers.txt.
シンボル | バージョン |
__kernel_rt_sigreturn | LINUX_2.6.39 |
__kernel_gettimeofday | LINUX_2.6.39 |
__kernel_clock_gettime | LINUX_2.6.39 |
__kernel_clock_getres | LINUX_2.6.39 |
For information on this code page, it's best to refer to the public
documentation:
http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:fixed-code
以下のテーブルは vDSO で公開されるシンボルの一覧である。
シンボル | バージョン |
__kernel_gettimeofday | LINUX_2.6 (Linux 4.4 以降で公開) |
__kernel_clock_gettime | LINUX_2.6 (Linux 4.4 以降で公開) |
シンボル | バージョン |
__kernel_sigtramp | LINUX_2.5 |
__kernel_syscall_via_break | LINUX_2.5 |
__kernel_syscall_via_epc | LINUX_2.5 |
The Itanium port is somewhat tricky. In addition to the vDSO above, it also has "light-weight system calls" (also known as "fast syscalls" or "fsys"). You can invoke these via the __kernel_syscall_via_epc vDSO helper. The system calls listed here have the same semantics as if you called them directly via syscall(2), so refer to the relevant documentation for each. The table below lists the functions available via this mechanism.
関数 |
clock_gettime |
getcpu |
getpid |
getppid |
gettimeofday |
set_tid_address |
Since it's just a raw page of code, there is no ELF information for doing symbol lookups or versioning. Simply call into the appropriate offset via the branch instruction, for example:
ble <offset>(%sr2, %r0)
オフセット | 関数 |
00b0 | lws_entry (CAS operations) |
00e0 | set_thread_pointer (used by glibc) |
0100 | linux_gateway_entry (syscall) |
シンボル | バージョン |
__kernel_clock_getres | LINUX_2.6.15 |
__kernel_clock_gettime | LINUX_2.6.15 |
__kernel_datapage_offset | LINUX_2.6.15 |
__kernel_get_syscall_map | LINUX_2.6.15 |
__kernel_get_tbfreq | LINUX_2.6.15 |
__kernel_getcpu * | LINUX_2.6.15 |
__kernel_gettimeofday | LINUX_2.6.15 |
__kernel_sigtramp_rt32 | LINUX_2.6.15 |
__kernel_sigtramp32 | LINUX_2.6.15 |
__kernel_sync_dicache | LINUX_2.6.15 |
__kernel_sync_dicache_p5 | LINUX_2.6.15 |
The CLOCK_REALTIME_COARSE and CLOCK_MONOTONIC_COARSE clocks are not supported by the __kernel_clock_getres and __kernel_clock_gettime interfaces; the kernel falls back to the real system call.
シンボル | バージョン |
__kernel_clock_getres | LINUX_2.6.15 |
__kernel_clock_gettime | LINUX_2.6.15 |
__kernel_datapage_offset | LINUX_2.6.15 |
__kernel_get_syscall_map | LINUX_2.6.15 |
__kernel_get_tbfreq | LINUX_2.6.15 |
__kernel_getcpu | LINUX_2.6.15 |
__kernel_gettimeofday | LINUX_2.6.15 |
__kernel_sigtramp_rt64 | LINUX_2.6.15 |
__kernel_sync_dicache | LINUX_2.6.15 |
__kernel_sync_dicache_p5 | LINUX_2.6.15 |
The CLOCK_REALTIME_COARSE and CLOCK_MONOTONIC_COARSE clocks are not supported by the __kernel_clock_getres and __kernel_clock_gettime interfaces; the kernel falls back to the real system call.
シンボル | バージョン |
__kernel_rt_sigreturn | LINUX_4.15 |
__kernel_gettimeofday | LINUX_4.15 |
__kernel_clock_gettime | LINUX_4.15 |
__kernel_clock_getres | LINUX_4.15 |
__kernel_getcpu | LINUX_4.15 |
__kernel_flush_icache | LINUX_4.15 |
シンボル | バージョン |
__kernel_clock_getres | LINUX_2.6.29 |
__kernel_clock_gettime | LINUX_2.6.29 |
__kernel_gettimeofday | LINUX_2.6.29 |
シンボル | バージョン |
__kernel_clock_getres | LINUX_2.6.29 |
__kernel_clock_gettime | LINUX_2.6.29 |
__kernel_gettimeofday | LINUX_2.6.29 |
シンボル | バージョン |
__kernel_rt_sigreturn | LINUX_2.6 |
__kernel_sigreturn | LINUX_2.6 |
__kernel_vsyscall | LINUX_2.6 |
シンボル | バージョン |
__kernel_sigreturn | LINUX_2.5 |
__kernel_rt_sigreturn | LINUX_2.5 |
__kernel_vsyscall | LINUX_2.5 |
__vdso_clock_gettime | LINUX_2.6 (Linux 3.15 以降で公開) |
__vdso_gettimeofday | LINUX_2.6 (Linux 3.15 以降で公開) |
__vdso_time | LINUX_2.6 (Linux 3.15 以降で公開) |
シンボル | バージョン |
__vdso_clock_gettime | LINUX_2.6 |
__vdso_getcpu | LINUX_2.6 |
__vdso_gettimeofday | LINUX_2.6 |
__vdso_time | LINUX_2.6 |
シンボル | バージョン |
__vdso_clock_gettime | LINUX_2.6 |
__vdso_getcpu | LINUX_2.6 |
__vdso_gettimeofday | LINUX_2.6 |
__vdso_time | LINUX_2.6 |
Linux のソースコードツリーのドキュメント、例、ソースコード:
Documentation/ABI/stable/vdso Documentation/ia64/fsys.txt Documentation/vDSO/* (vDSO の使用例がある)
find arch/ -iname '*vdso*' -o -iname '*gate*'