Celui qui n'existe pas, et renvoie donc -ENOSYS rapidement.
Depuis arch/x86/entry/entry_64.S :
#if __SYSCALL_MASK == ~0
cmpq $__NR_syscall_max, %rax
#else
andl $__SYSCALL_MASK, %eax
cmpl $__NR_syscall_max, %eax
#endif
ja 1f /* return -ENOSYS (already in pt_regs->ax) */
movq %r10, %rcx
/*
* This call instruction is handled specially in stub_ptregs_64.
* It might end up jumping to the slow path. If it jumps, RAX
* and all argument registers are clobbered.
*/
#ifdef CONFIG_RETPOLINE
movq sys_call_table(, %rax, 8), %rax
call __x86_indirect_thunk_rax
#else
call *sys_call_table(, %rax, 8)
#endif
.Lentry_SYSCALL_64_after_fastpath_call:
movq %rax, RAX(%rsp)
1:
Utilisez un numéro d'appel système invalide pour que le code de répartition revienne simplement avec
eax = -ENOSYS
au lieu d'être envoyé à une fonction de gestion des appels système.
Sauf si cela amène le noyau à utiliser le iret
chemin lent au lieu de sysret
/ sysexit
. Cela pourrait expliquer les mesures montrant un nombre invalide étant 17 cycles plus lents que syscall(SYS_getpid)
, car la gestion des erreurs de la glibc (réglage errno
) ne l'explique probablement pas. Mais d'après ma lecture de la source du noyau, je ne vois aucune raison pour laquelle il n'utiliserait toujours pas sysret
en retournant -ENOSYS
.
Cette réponse est pour sysenter
, pas syscall
. La question disait à l'origine sysenter
/ sysret
(ce qui était bizarre car sysexit
va avec sysenter
, tandis que sysret
va avec syscall
). J'ai répondu sur la base de sysenter
pour un processus 32 bits sur un noyau x86-64.
syscall
natif 64 bits est géré plus efficacement à l'intérieur du noyau. (Mise à jour ; avec les correctifs d'atténuation Meltdown / Spectre, il est toujours distribué via C do_syscall_64
en 4.16-rc2).
Que se passe-t-il si vous utilisez l'ABI Linux int 0x80 32 bits dans du code 64 bits ? Q&A donne un aperçu du côté noyau des points d'entrée des appels système du mode compat dans un noyau x86-64 (entry_64_compat.S
). Cette réponse ne prend que les parties pertinentes de cela.
Les liens dans cette réponse et celui-ci sont vers les sources Linux 4.12, qui ne contiennent pas la manipulation de table de page d'atténuation Meltdown, donc ce sera significatif surcharge supplémentaire.
int 0x80
et sysenter
ont des points d'entrée différents. Vous recherchez entry_SYSENTER_compat
. AFAIK, sysenter
y va toujours, même si vous l'exécutez dans un processus d'espace utilisateur 64 bits. Le point d'entrée de Linux pousse une constante __USER32_CS
comme valeur CS enregistrée, il reviendra donc toujours dans l'espace utilisateur en mode 32 bits.
Après avoir poussé les registres pour construire un struct pt_regs
sur la pile du noyau, il y a un TRACE_IRQS_OFF
crochet (aucune idée du nombre d'instructions que cela représente), puis call do_fast_syscall_32
qui est écrit en C. (natif 64 bits syscall
la répartition est effectuée directement depuis asm, mais les appels système compatibles 32 bits sont toujours répartis via C).
do_syscall_32_irqs_on
en arch/x86/entry/common.c
est assez léger :il suffit de vérifier si le processus est tracé (je pense que c'est ainsi que strace
peut accrocher les appels système via ptrace
), puis
...
if (likely(nr < IA32_NR_syscalls)) {
regs->ax = ia32_sys_call_table[nr]( ... arg );
}
syscall_return_slowpath(regs);
}
AFAIK, le noyau peut utiliser sysexit
après le retour de cette fonction.
Ainsi, le chemin de retour est le même, que EAX ait ou non un numéro d'appel système valide, et il est évident que le retour sans répartition est le chemin le plus rapide à travers cette fonction, en particulier dans un noyau avec atténuation Spectre où la branche indirecte sur la table des pointeurs de fonction passerait par une retpoline et ferait toujours une mauvaise prédiction.
Si vous voulez vraiment tester sysenter/sysexit sans toute cette surcharge supplémentaire, vous devrez modifier Linux pour mettre un point d'entrée beaucoup plus simple sans vérifier le traçage ou pousser/sauter tous les registres.
Vous voudriez probablement aussi modifier l'ABI pour passer une adresse de retour dans un registre (comme syscall
fait tout seul) au lieu d'être enregistré sur la pile de l'espace utilisateur que le sysenter
actuel de Linux ABI le fait ; il doit get_user()
pour lire la valeur EIP à laquelle il doit revenir.
Si tous ces frais généraux font partie de ce que vous voulez mesurer, vous êtes définitivement prêt avec un eax qui vous donne -ENOSYS
; au pire, vous obtiendrez une branche supplémentaire manquante à partir de la vérification de plage si les prédicteurs de branche sont chauds pour cette branche basée sur des appels système 32 bits normaux.
Dans ce benchmark de Brendan Gregg (lié à cet article de blog qui est une lecture intéressante sur le sujet) close(999)
(ou un autre fd non utilisé) est recommandé.