NOM

vdso – Présentation de l’objet partagé dynamique ELF virtuel

SYNOPSIS

#include <sys/auxv.h>
void *vdso = (uintptr_t) getauxval(AT_SYSINFO_EHDR);

DESCRIPTION

Le « vDSO » (objet partagé dynamique virtuel, « virtual dynamic shared object ») est une petite bibliothèque partagée que le noyau projette automatiquement dans l’espace d’adresses de toutes les applications en espace utilisateur. Les applications n’ont normalement pas besoin de s’occuper elles-mêmes de ces détails puisque le vDSO est d’habitude appelé par la bibliothèque C. Ainsi, vous pouvez écrire du code normalement en utilisant les fonctions standards et la bibliothèque C s’occupera d’utiliser toutes les fonctionnalités disponibles par l’intermédiaire du vDSO.
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.

Contexte exemple

Réaliser des appels système peut être lent. Dans les systèmes 32 bits x86, vous pouvez déclencher une interruption logicielle ( int $0x80) pour indiquer au noyau que vous voulez faire un appel système. Cependant, cette instruction est coûteuse : elle passe par tous les chemins complets de traitement des interruptions dans le microcode du processeur ainsi que dans le noyau. Les nouveaux processeurs ont des instructions plus rapides (mais non rétrocompatibles) pour initier les appels système. Plutôt que forcer la bibliothèque C à vérifier si cette fonctionnalité est disponible au moment de l’exécution, la bibliothèque C peut utiliser les fonctions fournies par le noyau dans le vDSO.
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.
One frequently used system call is gettimeofday(2). This system call is called both directly by user-space applications as well as indirectly by the C library. Think timestamps or timing loops or polling—all of these frequently need to know what time it is right now. This information is also not secret—any application in any privilege mode (root or any unprivileged user) will get the same answer. Thus the kernel arranges for the information required to answer this question to be placed in memory the process can access. Now a call to gettimeofday(2) changes from a system call to a normal function call and a few memory accesses.

Trouver le vDSO

L’adresse de base du vDSO (s’il existe) est passée par le noyau à tous les programmes dans le vecteur auxiliaire initial (consultez getauxval(3)) à l’aide de l’indicateur AT_SYSINFO_EHDR.
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.

Format de fichier

Puisque le vDSO est une image ELF complète, vous pouvez y rechercher des symboles. Cela permet d’ajouter de nouveaux symboles avec les versions de noyau plus récentes et permet à la bibliothèque C de détecter les fonctionnalités disponibles au moment de l’exécution lors de l’exécution sous différentes versions de noyau. D’habitude, la bibliothèque C fera la détection lors du premier appel puis mettra en cache le résultat pour les appels suivants.
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.

NOTES

Source

Lors de la compilation du noyau, le code vDSO est compilé et lié automatiquement. Il se trouve souvent dans le répertoire spécifique à l’architecture :

find arch/$ARCH/ -name '*vdso*.so*' -o -name '*gate*.so*'

Noms vDSO

Le nom du vDSO dépend des architectures. Il est souvent visible dans des endroits comme la sortie de ldd(1) de la glibc. Le nom exact ne devrait affecter aucun code, donc pas la peine de le coder en dur.
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

strace(1),_seccomp(2)_et_le_vDSO">strace(1),_seccomp(2)_et_le_vDSO">strace(1), seccomp(2) et le vDSO

Lors du suivi des appels système avec strace(1), les symboles (appels système) qui sont exportés par le vDSO n’apparaîtront pas dans la sortie du suivi. De même, ces appels système ne seront pas visibles par les filtres seccomp(2).

NOTES SPÉCIFIQUES AUX ARCHITECTURES

Les sous-sections suivantes fournissent des notes spécifiques aux architectures sur le vDSO.
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.

Fonctions ARM

Le tableau suivant indique les symboles exportés par le vDSO.
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.
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.rst.

Fonctions aarch64

Le tableau suivant indique les symboles exportés par le vDSO.
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

Fonctions bfin (Blackfin) (portage supprimé dans Linux 4.17)

As this CPU lacks a memory management unit (MMU), it doesn't set up a vDSO in the normal sense. Instead, it maps at boot time a few raw functions into a fixed location in memory. User-space applications then call directly into that region. There is no provision for backward compatibility beyond sniffing raw opcodes, but as this is an embedded CPU, it can get away with things—some of the object formats it runs aren't even ELF based (they're bFLT/FLAT).
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

Fonctions mips

Le tableau suivant indique les symboles exportés par le vDSO.
symbole version
__kernel_gettimeofday LINUX_2.6 (exportation depuis Linux 4.4)
__kernel_clock_gettime LINUX_2.6 (exportation depuis Linux 4.4)

Fonctions ia64 (Itanium)

Le tableau suivant indique les symboles exportés par le vDSO.
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

Fonctions parisc (hppa)

Le portage parisc à une page de code pleine de fonctions utilitaires appelée une page passerelle. Plutôt que d’utiliser l’approche classique du vecteur auxiliaire ELF, il passe l’adresse de la page au processus à l’aide du registre SR2. Les permissions sur la page sont telles qu’exécuter simplement ces adresses s’exécute automatiquement avec les droits du noyau et pas en espace utilisateur. C’est ainsi afin de correspondre au mode de fonctionnement HP-UX.
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)

Fonctions ppc/32

Le tableau suivant indique les symboles exportés par le vDSO. Les fonctions marquées avec un * ne sont disponibles que si le noyau est PowerPC64 (64 bits).
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
Before Linux 5.6, 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.

Fonctions ppc/64

Le tableau suivant indique les symboles exportés par le vDSO.
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
Before Linux 4.16, 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.

Fonctions riscv

Le tableau suivant indique les symboles exportés par le vDSO.
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

Fonctions s390

Le tableau suivant indique les symboles exportés par le vDSO.
symbole version
__kernel_clock_getres LINUX_2.6.29
__kernel_clock_gettime LINUX_2.6.29
__kernel_gettimeofday LINUX_2.6.29

Fonctions s390x

Le tableau suivant indique les symboles exportés par le vDSO.
symbole version
__kernel_clock_getres LINUX_2.6.29
__kernel_clock_gettime LINUX_2.6.29
__kernel_gettimeofday LINUX_2.6.29

Fonctions sh (SuperH)

Le tableau suivant indique les symboles exportés par le vDSO.
symbole version
__kernel_rt_sigreturn LINUX_2.6
__kernel_sigreturn LINUX_2.6
__kernel_vsyscall LINUX_2.6

Fonctions i386

Le tableau suivant indique les symboles exportés par le vDSO.
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)

Fonctions x86-64

Le tableau suivant indique les symboles exportés par le vDSO. Tous ces symboles sont aussi disponibles sans le préfixe « __vdso_ », mais vous devriez les ignorer et vous cantonner aux noms suivants.
symbole version
__vdso_clock_gettime LINUX_2.6
__vdso_getcpu LINUX_2.6
__vdso_gettimeofday LINUX_2.6
__vdso_time LINUX_2.6

Fonctions x86/x32

Le tableau suivant indique les symboles exportés par le vDSO.
symbole version
__vdso_clock_gettime LINUX_2.6
__vdso_getcpu LINUX_2.6
__vdso_gettimeofday LINUX_2.6
__vdso_time LINUX_2.6

Historique

The vDSO was originally just a single function—the vsyscall. In older kernels, you might see that name in a process's memory map rather than "vdso". Over time, people realized that this mechanism was a great way to pass more functionality to user space, so it was reconceived as a vDSO in the current format.

VOIR AUSSI

syscalls(2), getauxval(3), proc(5)
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/* (includes examples of using the vDSO)
find arch/ -iname '*vdso*' -o -iname '*gate*'

TRADUCTION

La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <[email protected]>, Thierry Vignaud <[email protected]>, François Micaux, Alain Portal <[email protected]>, Jean-Philippe Guérard <[email protected]>, Jean-Luc Coulon (f5ibh) <[email protected]>, Julien Cristau <[email protected]>, Thomas Huriaux <[email protected]>, Nicolas François <[email protected]>, Florentin Duneau <[email protected]>, Simon Paillard <[email protected]>, Denis Barbier <[email protected]>, David Prévot <[email protected]> et Jean-Paul Guillonneau <[email protected]>
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 à [email protected]