Takový, který neexistuje, a proto rychle vrátí -ENOSYS.
Z 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:
Použijte neplatné systémové telefonní číslo, aby se kód pro odbavení vrátil pouze s
eax = -ENOSYS místo toho, aby se vůbec odesílalo do funkce zpracování systémových volání.
Pokud to nezpůsobí, že jádro použije iret pomalá cesta místo sysret / sysexit . To by mohlo vysvětlit, že měření ukazující neplatné číslo je o 17 cyklů pomalejší než syscall(SYS_getpid) , protože zpracování chyb glibc (nastavení errno ) to asi nevysvětluje. Ale podle mého čtení zdrojového kódu jádra nevidím žádný důvod, proč by stále nepoužíval sysret při návratu -ENOSYS .
Tato odpověď je pro sysenter , nikoli syscall . Otázka původně zněla sysenter / sysret (což bylo divné, protože sysexit jde s sysenter , zatímco sysret jde s syscall ). Odpověděl jsem na základě sysenter pro 32bitový proces na jádře x86-64.
Nativní 64bitová verze syscall je v jádře zpracováno efektivněji. (Aktualizace; se záplatami na zmírnění Meltdown / Spectre se stále odesílá přes C do_syscall_64 v 4.16-rc2).
Můj Co se stane, když použijete 32bitové int 0x80 Linux ABI v 64bitovém kódu? Otázky a odpovědi poskytují přehled o straně jádra vstupních bodů systémových volání z režimu compat do jádra x86-64 (entry_64_compat.S ). Tato odpověď pouze přebírá příslušné části.
Odkazy v této odpovědi a toto jsou na zdroje Linuxu 4.12, které neobsahují manipulaci s tabulkou stránek zmírňující Meltdown, takže to bude významné další režie.
int 0x80 a sysenter mají různé vstupní body. Hledáte entry_SYSENTER_compat . AFAIK, sysenter vždy tam jde, i když jej spustíte v 64bitovém procesu v uživatelském prostoru. Vstupní bod Linuxu tlačí konstantu __USER32_CS jako uloženou hodnotu CS, takže se vždy vrátí do uživatelského prostoru v 32bitovém režimu.
Po zatlačení registrů k vytvoření struct pt_regs v zásobníku jádra je TRACE_IRQS_OFF háček (nemám ponětí, kolik instrukcí to obnáší), pak call do_fast_syscall_32 který je napsán v C. (Nativní 64bitový syscall odesílání se provádí přímo z asm, ale volání 32bitového kompatibilního systému jsou vždy odesílána přes C).
do_syscall_32_irqs_on v arch/x86/entry/common.c je docela lehký:stačí zkontrolovat, zda je proces sledován (myslím, že takto strace může připojit systémová volání přes ptrace ), pak
...
if (likely(nr < IA32_NR_syscalls)) {
regs->ax = ia32_sys_call_table[nr]( ... arg );
}
syscall_return_slowpath(regs);
}
AFAIK, jádro může používat sysexit poté, co se tato funkce vrátí.
Návratová cesta je tedy stejná bez ohledu na to, zda má EAX platné číslo systémového volání či nikoli, a očividně návrat bez odeslání je nejrychlejší cestou přes tuto funkci, zejména v jádře se zmírněním Spectre, kde nepřímá větev na tabulce funkčních ukazatelů by prošel retpolínou a vždy špatně předpověděl.
Pokud chcete opravdu otestovat sysenter/sysexit bez všech těch dalších režií, budete muset upravit Linux tak, aby vložil mnohem jednodušší vstupní bod bez kontroly sledování nebo tlačení / otevírání všech registrů.
Pravděpodobně byste také chtěli upravit ABI tak, aby předávalo zpáteční adresu v registru (například syscall dělá sám o sobě) místo toho, aby se ukládal do zásobníku uživatelského prostoru, což je aktuální Linux sysenter ABI ano; musí být get_user() číst hodnotu EIP, ke které by se měla vrátit.
Pokud je celá tato režie součástí toho, co chcete změřit, určitě máte připraveno eax, které vám poskytne -ENOSYS; v nejhorším případě dostanete z kontroly dosahu jednu chybnou větev navíc, pokud jsou prediktory větve pro tuto větev horké na základě normálních 32bitových systémových volání.
V tomto benchmarku Brendana Gregga (odkaz na tento blogový příspěvek, který je zajímavým čtením na toto téma) close(999) (nebo jiné nepoužívané fd).