## Overview

When a syscall is invoked by an application, execution passes through several stages.
In order to reduce boilerplate code, syscall-specific logic is generated with the
[zither](https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/tools/zither/)
tool. Before diving into how each of the above stages works, it is best to understand
how this code is generated.

![Diagram of User Mode and Kernel Mode blocks and how specific code blocks are created.](https://fuchsia.dev/static/fuchsia-src/concepts/kernel/images/overview.png)

## Code generation with zither

Fuchsia syscalls are declared in FIDL files in [//zircon/vdso](https://cs.opensource.google/fuchsia/fuchsia/+/main:/zircon/vdso).

Example declaration of `zx_channel_create`:

    library zx;

    @transport("Syscall")
    protocol channel {
        channel_create(struct {
            options uint32;
        }) -> (resource struct {
            status status;
            out0 handle;
            out1 handle;
        });
    };

When the kernel is built,
[fidlc](https://fuchsia.dev/docs/reference/fidl/language/fidlc) (the FIDL
front-end) takes these FIDL files and generates FIDL Intermediate Representation
(IR) JSON files. The IR file is generated at `//out/default/gen/zircon/vdso/zx.fidl.json`.

zither reads this IR file, and generates source files that invoke C++ macros
with the inputs and outputs of each syscall. These macros are defined
per-architecture to allow zither to output architecture-agnostic source.

Example zither output:

    KERNEL_SYSCALL(channel_create, zx_status_t, /* no attributes */, 3,
        (options, out0, out1), (
        uint32_t options,
        _ZX_SYSCALL_ANNO(acquire_handle("Fuchsia")) zx_handle_t* out0,
        _ZX_SYSCALL_ANNO(acquire_handle("Fuchsia")) zx_handle_t* out1))

Example x86 implementation:

    #define KERNEL_SYSCALL(name, type, attrs, nargs, arglist, prototype) \
      m_syscall zx_##name, ZX_SYS_##name, nargs, 1

    .macro m_syscall name, num, nargs, public
    syscall_entry_begin \name
        .cfi_same_value %r12
        .cfi_same_value %r13
    .if \nargs <= 3
        zircon_syscall \num, \name, \name
        ret
    .endif

    .macro zircon_syscall num, name, caller
        mov $\num, %eax
        syscall
    // This symbol at the return address identifies this as an approved call site.
        .hidden CODE_SYSRET_\name\()_VIA_\caller
    CODE_SYSRET_\name\()_VIA_\caller\():
    .endm

This pattern of interfacing zither-generated source with C++ macros and
assembly routines is found throughout the syscall stages.

## Program

In order to use a syscall, you should include the
`<zircon/syscalls.h>` header from the Fuchsia SDK, which is generated by zither.
While the header is available to the program during compilation, the
implementation is only available during runtime inside the vDSO.

## vDSO

The virtual Dynamic Shared Object
([vDSO](https://fuchsia.dev/docs/concepts/kernel/vdso)) is an ELF file
containing the user-space implementation of each syscall. The assembly routines
in the vDSO are mostly generated by zither, but all have this same structure:

1. Save user-provided arguments to architecture-specific registers
2. Store the syscall number to an architecture-specific register (`%eax` for x86)
3. Switch context to the kernel (`syscall` for x86)

The routines in the vDSO can be viewed with:

    $ objdump -d `find out/default.zircon -name libzircon.so.debug` | less

x86 implementation of `zx_channel_create`:

    0000000000007a70 <_zx_channel_create>:
        7a70:       b8 03 00 00 00          mov    $0x3,%eax
        7a75:       0f 05                   syscall

    0000000000007a77 <CODE_SYSRET_zx_channel_create_VIA_zx_channel_create>:
        7a77:       c3                      retq

When the kernel is built, the vDSO is packaged as an ELF file alongside the
kernel. During boot,
[physboot](https://cs.opensource.google/fuchsia/fuchsia/+/main:/zircon/kernel/phys/handoff-prep/handoff-prep.cc) hands the vDSO
image off to the kernel in a
[vmo](https://fuchsia.dev/docs/reference/kernel_objects/vm_object), from which the kernel
[creates the vDSO](https://cs.opensource.google/fuchsia/fuchsia/+/main:/zircon/kernel/lib/userabi/vdso.rs).
During boot-up, some constants are written into the vDSO to allow user-space
programs to query these constants without doing a hop to the kernel.

Before your program's entrypoint is called, `ld.so` maps the vDSO into memory.
To prevent [return-to-libc](https://en.wikipedia.org/wiki/Return-to-libc_attack)
attacks, the vDSO is placed at a random location in the process' address space,
and the base address is provided to the first thread in a specific register.

The vDSO is dynamically linked to the user's program in the entrypoint provided
by [libc](https://fuchsia.dev/docs/development/languages/c-cpp/libc)

![Sequence diagram of process loading and the vDSO](https://fuchsia.dev/static/fuchsia-src/concepts/kernel/images/vdso_loading.png)

## Syscall handler

In order to receive the syscall in privileged mode, the kernel registers the
syscall handler at startup. When this routine is called, the syscall
number is used to index into a map of dispatch routines.

x86 implementation: [//zircon/kernel/arch/x86](https://cs.opensource.google/fuchsia/fuchsia/+/main:/zircon/kernel/arch/x86)

    write_msr(X86_MSR_IA32_LSTAR, (uint64_t)&x86_syscall);

    FUNCTION_LABEL(x86_syscall)
      leaq    .Lcall_wrapper_table(%rip), %r11
      movq    (%r11,%rax,8), %r11
      jmp     *%r11
    END_FUNCTION(x86_syscall)

The dispatch routines are responsible for moving the syscall args to appropriate
registers to be picked up as C function arguments, and calling the wrapper
function. The routines are generated at build-time using the `syscall_dispatch`
macro and a zither-generated source file.

    #define KERNEL_SYSCALL(name, type, attrs, nargs, arglist, prototype) \
      syscall_dispatch nargs, name

    KERNEL_SYSCALL(channel_create, zx_status_t, /* no attributes */, 3,
        (options, out0, out1), (
        uint32_t options,
        _ZX_SYSCALL_ANNO(acquire_handle("Fuchsia")) zx_handle_t* out0,
        _ZX_SYSCALL_ANNO(acquire_handle("Fuchsia")) zx_handle_t* out1))

    .macro syscall_dispatch nargs, syscall
      LOCAL_FUNCTION(.Lcall_\syscall\())
        // move args around
        pre_\nargs\()_args
        // calls wrapper
        call wrapper_\syscall
        // cleans up
        post_\nargs\()_args
      END_FUNCTION(.Lcall_\syscall\())
    .endm

## Syscall wrapper

The syscall wrapper functions are generated by zither, and are responsible for
sanitizing the raw register arguments, calling the syscall implementation, then
copying any handles back to the client. These wrappers have the naming
convention `wrapper_<syscall>` and are Rust functions included into
[//zircon/kernel/lib/syscalls/syscalls.rs](https://cs.opensource.google/fuchsia/fuchsia/+/main:/zircon/kernel/lib/syscalls/syscalls.rs),
which also defines the shared `do_syscall` preamble and postamble.

    #[unsafe(no_mangle)]
    pub unsafe extern "C" fn wrapper_channel_create(
        arg_options: u64,
        arg_out0: u64,
        arg_out1: u64,
        pc: u64,
    ) -> SyscallResult {
        let make_call = move || {
            let mut out_handle_out0 = HandleValue::new(ZX_HANDLE_INVALID);
            let mut out_handle_out1 = HandleValue::new(ZX_HANDLE_INVALID);
            let mut result = unsafe {
                sys_channel_create(
                    SafeSyscallArgument::sanitize(arg_options),
                    &mut out_handle_out0,
                    &mut out_handle_out1,
                )
            };
            // Complete copy-out of output handles.
            if result.is_err() {
                core::hint::cold_path();
                return result.into_raw_return();
            }
            result = UserOutPtr::<zx_handle_t>::sanitize(arg_out0)
                .copy_to_user(&out_handle_out0.raw_value());
            if result.is_err() {
                core::hint::cold_path();
                thread::signal_policy_exception(ZX_EXCP_POLICY_CODE_HANDLE_LEAK, 0);
            }
            result = UserOutPtr::<zx_handle_t>::sanitize(arg_out1)
                .copy_to_user(&out_handle_out1.raw_value());
            if result.is_err() {
                core::hint::cold_path();
                thread::signal_policy_exception(ZX_EXCP_POLICY_CODE_HANDLE_LEAK, 0);
            }
            result.into_raw_return()
        };
        unsafe { do_syscall(ZX_SYS_CHANNEL_CREATE, pc, ValidSyscallPC::channel_create, make_call) }
    }

## Syscall implementation

The syscall implementation is a hand-written function with the naming convention
`sys_<syscall>`. These functions contain the core logic of the syscall, and are
architecture-agnostic.

[//zircon/kernel/lib/syscalls/channel.rs](https://cs.opensource.google/fuchsia/fuchsia/+/main:/zircon/kernel/lib/syscalls/channel.rs)

    #[syscall]
    pub fn sys_channel_create(
        options: u32,
        out0: &mut HandleValue,
        out1: &mut HandleValue,
    ) -> Result<(), Status> {
        ...
    }

## Appendix

[Example commit](https://fuchsia-review.googlesource.com/c/fuchsia/+/431659) for
creating a new syscall.
[](https://issues.fuchsia.dev/issues/new?component=1439230&template=1881640&description=Issue+on:+https://fuchsia.dev/docs/concepts/kernel/life_of_a_syscall.md "Report a bug or suggest improvements on this page") [](https://cs.opensource.google/fuchsia/fuchsia/+/main:docs/concepts/kernel/life_of_a_syscall.md "View the source code of this page") [](https://ci.android.com/edit?repo=fuchsia/fuchsia/main&file=docs/concepts/kernel/life_of_a_syscall.md&cloneRepo=fuchsia/fuchsia:refs/heads/main&fullPath=fuchsia/docs/concepts/kernel/life_of_a_syscall.md "Edit the source code of this page")