/* IRQs are off. */ movq %rsp, %rdi /* Sign extend the lower 32bit as syscall numbers are treated as int */ movslq %eax, %rsi
/* clobbers %rax, make sure it is after saving the syscall nr */ IBRS_ENTER UNTRAIN_RET CLEAR_BRANCH_HISTORY
call do_syscall_64 /* returns with IRQs disabled */
/* * Try to use SYSRET instead of IRET if we're returning to * a completely clean 64-bit userspace context. If we're not, * go to the slow exit path. * In the Xen PV case we must use iret anyway. */
ALTERNATIVE "testb %al, %al; jz swapgs_restore_regs_and_return_to_usermode", \ "jmp swapgs_restore_regs_and_return_to_usermode", X86_FEATURE_XENPV
/* * We win! This label is here just for ease of understanding * perf profiles. Nothing jumps here. */ syscall_return_via_sysret: IBRS_EXIT POP_REGS pop_rdi=0
/* * Now all regs are restored except RSP and RDI. * Save old stack pointer and switch to trampoline stack. */ movq %rsp, %rdi movq PER_CPU_VAR(cpu_tss_rw + TSS_sp0), %rsp UNWIND_HINT_END_OF_STACK
if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) { /* Invalid system call, but still a system call. */ regs->ax = __x64_sys_ni_syscall(regs); }
/* * Check that the register state is valid for using SYSRET to exit * to userspace. Otherwise use the slower but fully capable IRET * exit path. */
/* XEN PV guests always use the IRET path */ if (cpu_feature_enabled(X86_FEATURE_XENPV)) returnfalse;
/* SYSRET requires RCX == RIP and R11 == EFLAGS */ if (unlikely(regs->cx != regs->ip || regs->r11 != regs->flags)) returnfalse;
/* CS and SS must match the values set in MSR_STAR */ if (unlikely(regs->cs != __USER_CS || regs->ss != __USER_DS)) returnfalse;
/* * On Intel CPUs, SYSRET with non-canonical RCX/RIP will #GP * in kernel space. This essentially lets the user take over * the kernel, since userspace controls RSP. * * TASK_SIZE_MAX covers all user-accessible addresses other than * the deprecated vsyscall page. */ if (unlikely(regs->ip >= TASK_SIZE_MAX)) returnfalse;
/* * SYSRET cannot restore RF. It can restore TF, but unlike IRET, * restoring TF results in a trap from userspace immediately after * SYSRET. */ if (unlikely(regs->flags & (X86_EFLAGS_RF | X86_EFLAGS_TF))) returnfalse;
/* Use SYSRET to exit to userspace */ returntrue; }
/** * syscall_enter_from_user_mode - Establish state and check and handle work * before invoking a syscall * @regs: Pointer to currents pt_regs * @syscall: The syscall number * * Invoked from architecture specific syscall entry code with interrupts * disabled. The calling code has to be non-instrumentable. When the * function returns all state is correct, interrupts are enabled and the * subsequent functions can be instrumented. * * This is the combination of enter_from_user_mode() and * syscall_enter_from_user_mode_work() to be used when there is no * architecture specific work to be done between the two. * * Returns: The original or a modified syscall number. See * syscall_enter_from_user_mode_work() for further explanation. */ static __always_inline longsyscall_enter_from_user_mode(struct pt_regs *regs, long syscall) { long ret;
enter_from_user_mode(regs);
instrumentation_begin(); local_irq_enable(); ret = syscall_enter_from_user_mode_work(regs, syscall); instrumentation_end();
/** * syscall_enter_from_user_mode_work - Check and handle work before invoking * a syscall * @regs: Pointer to currents pt_regs * @syscall: The syscall number * * Invoked from architecture specific syscall entry code with interrupts * enabled after invoking enter_from_user_mode(), enabling interrupts and * extra architecture specific work. * * Returns: The original or a modified syscall number * * If the returned syscall number is -1 then the syscall should be * skipped. In this case the caller may invoke syscall_set_error() or * syscall_set_return_value() first. If neither of those are called and -1 * is returned, then the syscall will fail with ENOSYS. * * It handles the following work items: * * 1) syscall_work flag dependent invocations of * ptrace_report_syscall_entry(), __secure_computing(), trace_sys_enter() * 2) Invocation of audit_syscall_entry() */ static __always_inline longsyscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall) { unsignedlong work = READ_ONCE(current_thread_info()->syscall_work);
if (work & SYSCALL_WORK_ENTER) syscall = syscall_trace_enter(regs, work);
static __always_inline longsyscall_trace_enter(struct pt_regs *regs, unsignedlong work) { long syscall, ret = 0;
/* * Handle Syscall User Dispatch. This must comes first, since * the ABI here can be something that doesn't make sense for * other syscall_work features. */ if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) { if (syscall_user_dispatch(regs)) return-1L; }
/* * User space got a time slice extension granted and relinquishes * the CPU. The work stops the slice timer to avoid an extra round * through hrtimer_interrupt(). */ if (work & SYSCALL_WORK_SYSCALL_RSEQ_SLICE) rseq_syscall_enter_work(syscall_get_nr(current, regs));
/* Handle ptrace */ if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) { ret = arch_ptrace_report_syscall_entry(regs); if (ret || (work & SYSCALL_WORK_SYSCALL_EMU)) return-1L; }
/* Do seccomp after ptrace, to catch any tracer changes. */ if (work & SYSCALL_WORK_SECCOMP) { ret = __secure_computing(); if (ret == -1L) return ret; }
/* Either of the above might have changed the syscall number */ syscall = syscall_get_nr(current, regs);
if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT)) syscall = trace_syscall_enter(regs, syscall);
syscall_enter_audit(regs, syscall);
return ret ? : syscall; }
那么在最后可以清楚地看到ebpf中TRACEPOINT的支持
1 2
if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT)) syscall = trace_syscall_enter(regs, syscall);
/* Out of line to prevent tracepoint code duplication */
longtrace_syscall_enter(struct pt_regs *regs, long syscall) { trace_sys_enter(regs, syscall); /* * Probes or BPF hooks in the tracepoint may have changed the * system call number. Reread it. */ return syscall_get_nr(current, regs); }
voidtrace_syscall_exit(struct pt_regs *regs, long ret) { trace_sys_exit(regs, ret); }
/* * Individual subsystem my have a separate configuration to * enable their tracepoints. By default, this file will create * the tracepoints if CONFIG_TRACEPOINTS is defined. If a subsystem * wants to be able to disable its tracepoints from being created * it can define NOTRACE before including the tracepoint headers. */ #ifdefined(CONFIG_TRACEPOINTS) && !defined(NOTRACE) #define TRACEPOINTS_ENABLED #endif