#include <sys/auxv.h> void *vdso = (uintptr_t) getauxval(AT_SYSINFO_EHDR);
Pourquoi le vDSO existe ? Certains appels système fournis par le noyau finissent par être utilisés fréquemment par le code en espace utilisateur, au point que ces appels peuvent avoir une emprise excessive sur les performances. C’est à la fois dû à la fréquence des appels qu’aux nombreux changements de contexte à force de sortir de l’espace utilisateur pour entrer dans le noyau.
La suite de cette documentation est orientée pour les curieux et les auteurs de la bibliothèque C plutôt que pour les développeurs généraux. Si vous essayez d’appeler le vDSO dans vos propres applications plutôt que d’utiliser la bibliothèque C, vous faites sans doute fausse route.
Remarquez que cette terminologie peut être source de confusion. Sur les systèmes x86, la fonction vDSO utilisée pour déterminer la méthode préférée pour réaliser un appel système est appelée « __kernel_vsyscall » alors que sous x86_64, le terme « vsyscall » se réfère aussi à une façon obsolète de demander au noyau l’heure ou le processeur sur lequel est l’appelant.
Un appel système fréquemment utilisé est gettimeofday(2). Cet appel système est appelé à la fois directement par les applications en espace utilisateur et indirectement par la bibliothèque C. Remarquez que les horodatages, boucles temporelles ou scrutations — ont tous fréquemment besoin de savoir l’heure exacte. Ce n’est pas non plus un secret — n’importe quelle application dans n’importe quel mode (superutilisateur ou utilisateur normal) aura la même réponse. Alors le noyau s’arrange pour que les informations nécessaires pour répondre à cette question soient placées dans la mémoire accessible au processus. Ainsi un appel de gettimeofday(2) est transformé d’un appel système en un appel normal de fonction, avec peu d’accès mémoire.
Vous ne devez pas supposer que le vDSO est projeté à un endroit particulier de la projection en mémoire de l’utilisateur. L’adresse de base sera normalement aléatoire au moment de l’exécution à chaque fois qu’une nouvelle image de processus est créée (au moment de execve(2)). C’est ainsi pour des raisons de sécurité, afin d’éviter les attaques de « retour vers libc ».
Pour certaines architectures, un indicateur AT_SYSINFO est aussi présent. Il n’est utilisé que pour localiser le point d’entrée vsyscall et est souvent omis ou défini à 0 (signifiant qu’il n’est pas disponible). Cet indicateur est un rappel du fonctionnement initial de vDSO (consultez Historique ci-dessous) et son utilisation devrait être évitée.
Tous les appels sont aussi versionnés (en utilisant le format de version GNU). Cela permet au noyau de mettre à jour la signature de fonction sans casser la rétrocompatibilité. Cela signifie modifier les arguments acceptés par la fonction et la valeur de retour. Ainsi, lors de la recherche de symboles dans le vDSO, vous devez toujours inclure la version pour correspondre à l’ABI attendue.
Typiquement, le vDSO suit la convention de nommage de préfixer tous les symboles par « __vdso_ » ou « __kernel_ » afin de les distinguer des autres symboles standards. Par exemple, la fonction « gettimeofday » est nommée « __vdso_gettimeofday ».
Utilisez les conventions d’appel C standard pour appeler n’importe laquelle de ces fonctions. Pas la peine de vous embêter avec les registres bizarres ou les comportements de pile.
find arch/$ARCH/ -name '*vdso*.so*' -o -name '*gate*.so*'
| ABI utilisateur | Nom 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 |
Remarquez que le vDSO utilisé est basé sur l’ABI du code en espace utilisateur et non sur l’ABI du noyau. Ainsi, par exemple, si vous exécutez un binaire ELF 32 bits i386, vous obtiendrez le même vDSO que vous l’exécutiez avec un noyau 32 bits i386 ou avec un noyau 64 bits x86_64. Par conséquent, le nom de l’ABI en espace utilisateur devrait être utilisé pour déterminer la section suivante adéquate.
| symbole | version |
| __vdso_gettimeofday | LINUX_2.6 (exported since Linux 4.1) |
| __vdso_clock_gettime | LINUX_2.6 (exported since Linux 4.1) |
De plus, le portage ARM a une page de code pleine de fonctions utilitaires. Puisque ce n’est qu’une page de code brut, aucune information ELF n’existe pour faire la recherche de symboles ou le versionnage. Elle fournit cependant une prise en charge pour plusieurs versions.
Pour des renseignements sur cette page de code, mieux vaut consulter la documentation du noyau puisqu’elle est extrêmement détaillée et couvre tous ce que vous devez savoir : Documentation/arm/kernel_user_helpers.rst.
| symbole | version |
| __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 |
Pour des renseignements sur cette page de code, mieux vaut consulter la
documentation publique :
http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:fixed-code
| symbole | version |
| __kernel_gettimeofday | LINUX_2.6 (exportation depuis Linux 4.4) |
| __kernel_clock_gettime | LINUX_2.6 (exportation depuis Linux 4.4) |
| symbole | version |
| __kernel_sigtramp | LINUX_2.5 |
| __kernel_syscall_via_break | LINUX_2.5 |
| __kernel_syscall_via_epc | LINUX_2.5 |
Le portage Itanium est un peu périlleux. En plus du vDSO ci-dessus, il a aussi des « appels système légers » (aussi appelés « appels système rapides » ou « fsys »). Ils peuvent être appelés à l’aide de l’assistant vDSO __kernel_syscall_via_epc. Les appels système indiqués ici ont la même sémantique que si vous les appeliez directement à l’aide de syscall(2), donc consultez la documentation adéquate pour chacun d’entre eux. Le tableau suivant indique les fonctions disponibles par ce mécanisme.
| fonction |
| clock_gettime |
| getcpu |
| getpid |
| getppid |
| gettimeofday |
| set_tid_address |
Puisque ce n’est qu’une page de code brut, aucune information ELF n’existe pour faire la recherche de symboles ou le versionnage. Appelez simplement l’adresse adéquate à l’aide de l’instruction de branche, par exemple :
ble <offset>(%sr2, %r0)
| offset | fonction |
| 00b0 | lws_entry (opérations CAS) |
| 00e0 | set_thread_pointer (utilisé par la glibc) |
| 0100 | linux_gateway_entry (syscall) |
| symbole | version |
| __kernel_clock_getres | LINUX_2.6.15 |
| __kernel_clock_gettime | LINUX_2.6.15 |
| __kernel_clock_gettime64 | LINUX_5.11 |
| __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 |
Avant Linux 5.6, les horloges CLOCK_REALTIME_COARSE et CLOCK_MONOTONIC_COARSE ne sont pas prises en charge par les interfaces __kernel_clock_getres et __kernel_clock_gettime. Le noyau a recours à l’appel système réel.
| symbole | version |
| __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 |
Avant Linux 4.16, les horloges CLOCK_REALTIME_COARSE et CLOCK_MONOTONIC_COARSE ne sont pas prises en charge par les interfaces __kernel_clock_getres et __kernel_clock_gettime. Le noyau a recours à l’appel système réel.
| symbole | version |
| __vdso_rt_sigreturn | LINUX_4.15 |
| __vdso_gettimeofday | LINUX_4.15 |
| __vdso_clock_gettime | LINUX_4.15 |
| __vdso_clock_getres | LINUX_4.15 |
| __vdso_getcpu | LINUX_4.15 |
| __vdso_flush_icache | LINUX_4.15 |
| symbole | version |
| __kernel_clock_getres | LINUX_2.6.29 |
| __kernel_clock_gettime | LINUX_2.6.29 |
| __kernel_gettimeofday | LINUX_2.6.29 |
| symbole | version |
| __kernel_clock_getres | LINUX_2.6.29 |
| __kernel_clock_gettime | LINUX_2.6.29 |
| __kernel_gettimeofday | LINUX_2.6.29 |
| symbole | version |
| __kernel_rt_sigreturn | LINUX_2.6 |
| __kernel_sigreturn | LINUX_2.6 |
| __kernel_vsyscall | LINUX_2.6 |
| symbole | version |
| __kernel_sigreturn | LINUX_2.5 |
| __kernel_rt_sigreturn | LINUX_2.5 |
| __kernel_vsyscall | LINUX_2.5 |
| __vdso_clock_gettime | LINUX_2.6 (exportation depuis Linux 3.15) |
| __vdso_gettimeofday | LINUX_2.6 (exportation depuis Linux 3.15) |
| __vdso_time | LINUX_2.6 (exportation depuis Linux 3.15) |
| symbole | version |
| __vdso_clock_gettime | LINUX_2.6 |
| __vdso_getcpu | LINUX_2.6 |
| __vdso_gettimeofday | LINUX_2.6 |
| __vdso_time | LINUX_2.6 |
| symbole | version |
| __vdso_clock_gettime | LINUX_2.6 |
| __vdso_getcpu | LINUX_2.6 |
| __vdso_gettimeofday | LINUX_2.6 |
| __vdso_time | LINUX_2.6 |
Les documents, exemples et le code source dans l’arborescence du code source de Linux :
Documentation/ABI/stable/vdso Documentation/ia64/fsys.rst Documentation/vDSO/* (contient des exemples d’utilisation du vDSO)
find arch/ -iname '*vdso*' -o -iname '*gate*'
Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.
Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à