NOM¶
vDSO - Panorama 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ée 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
la 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.
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 — qui ont tous fréquemment
besoin de savoir l’heure exacte. Ce n’est pas non plus un
secret — de n’importe quelle application dans
n’importe quel mode (superutilisateur ou utilisateur normal) auront
tous 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 un peu
d’accès mémoire.
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 du type
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 type
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). Ce type est un rappel
du fonctionnement initial de vDSO (consultez
Historique ci-dessous) et
son utilisation devrait être évitée.
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, la 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 |
ia64 |
linux-gate.so.1 |
ppc/32 |
linux-vdso32.so.1 |
ppc/64 |
linux-vdso64.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 |
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 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 de la recherche de symboles ou du versionnement.
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.txt.
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)¶
Comme ce processeur n’a pas d’unité de gestion
mémoire (MMU), il ne définit pas de vDSO au sens usuel. À
la place, il projette au démarrage quelques fonctions brutes à
un endroit spécifique de la mémoire. Les applications en espace
utilisateur appellent ensuite directement dans cette zone. Aucune mesure de
rétrocompatibilité n’est prise à part en sniffant
les codes opératoires bruts, mais comme il s’agit d’un
processeur embarqué, il peut s’en sortir impunément
– certains formats d’objet qu’il exécute ne
sont même pas basés sur ELF (ils sont 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 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 en à 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 de la recherche de symboles ou du versionnement.
Appelez simplement l’adresse adéquate à l’aide de
l’instruction de branche, par exemple :
ble <adresse>(%sr2, %r0)
adresse |
fonction |
|
00b0 |
lws_entry |
00e0 |
set_thread_pointer |
0100 |
linux_gateway_entry (syscall) |
0268 |
syscall_nosys |
0274 |
tracesys |
0324 |
tracesys_next |
0368 |
tracesys_exit |
03a0 |
tracesys_sigexit |
03b8 |
lws_start |
03dc |
lws_exit_nosys |
03e0 |
lws_exit |
03e4 |
lws_compare_and_swap64 |
03e8 |
lws_compare_and_swap |
0404 |
cas_wouldblock |
0410 |
cas_action |
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_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 |
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 |
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 |
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¶
Le vDSO n’était à l’origine qu’une seule
fonction — le vsyscall. Dans les anciens noyaux, ce nom pourrait
être vu dans une projection en mémoire de processus à la
place de « vdso ». Le temps passant, les gens ont
réalisé que ce mécanisme était un excellent moyen
pour passer plus de fonctionnalités à l’espace
utilisateur, il a donc été reconçu en tant que vDSO au
format actuel.
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.txt
Documentation/vDSO/* (contient des exemples d’utilisation du vDSO)
find arch/ -iname '*vdso*' -o -iname '*gate*'
COLOPHON¶
Cette page fait partie de la publication 3.65 du projet
man-pages Linux.
Une description du projet et des instructions pour signaler des anomalies
peuvent être trouvées à l'adresse
http://www.kernel.org/doc/man-pages/.
TRADUCTION¶
Depuis 2010, cette traduction est maintenue à l'aide de l'outil po4a
<
http://po4a.alioth.debian.org/> par l'équipe de traduction
francophone au sein du projet perkamon
<
http://perkamon.alioth.debian.org/>.
Veuillez signaler toute erreur de traduction en écrivant à
<debian-l10n-french@lists.debian.org> ou par un rapport de bogue sur le
paquet
manpages-fr.
Vous pouvez toujours avoir accès à la version anglaise de ce
document en utilisant la commande «
man -L C
<section>
<page_de_man> ».