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).