Updated: 2022/Sep/29

Please read Privacy Policy. It's for your privacy.


VERSIONINGSYSCALLS(9)      Kernel Developer's Manual     VERSIONINGSYSCALLS(9)

NAME
     versioningsyscalls - guide on versioning syscalls

DESCRIPTION
     NetBSD has the ability to change the ABI of a syscall whilst retaining
     backwards compatibility with existing code.  This means that existing
     code keeps working the same way as before, and new code can use new
     features and/or functionality.  In the past this has allowed dev_t to
     move from 16 bits to 32 bits, ino_t and time_t to move from 32 bits to 64
     bits, and adding fields to struct kevent without disturbing existing
     binaries.  To achieve this both kernel and userland changes are required.

     In the kernel, a new syscall is added with a new ABI, and the old syscall
     is retained and moved to a new location that holds the compatibility
     syscalls (src/sys/compat).  Kernels can be compiled with or without
     backwards compatibility syscalls.  See the COMPAT_XX options in
     options(4).

     In userland, the original syscall stub is moved into src/lib/libc/compat
     retaining the same symbol name and ABI.  The new stub is added to libc,
     and in the header file the syscall symbol is made to point to the new
     name with the new ABI.

     This is done via symbol renaming instead of ELF versioned symbols for
     historical reasons.  NetBSD has retained binary compatibility with most
     syscalls since NetBSD 0.9 with the exception of Scheduler Activation
     syscalls which are not being emulated because of the cost and safety of
     doing so.

     To avoid confusion, the following words are used to disambiguate which
     version of the system call is being described.

           old         Any previous versions of the syscall, which have
                       already been versioned and superseded by the current
                       version of the syscall.

           current     The version of the syscall currently in use.

           next        The version of the syscall that will become standard in
                       the next release.

     Additionally, XYZ always represents the last NetBSD release where the
     current version of the system call is the default, multiplied by ten and
     retaining a leading zero.  For example NetBSD 0.9 has COMPAT_09 whereas
     NetBSD 10.0 has COMPAT_100.

VERSIONING THE SYSCALL
     This section describes what needs to be modified to add the new version
     of the syscall.  It assumes the current version of the syscall is
     my_syscall(struct my_struct *ms) and that my_struct will be versioned.
     If not versioning a struct, passages that mention my_struct can be
     ignored.

   Versioning structs
     To version struct my_struct, first make a copy of my_struct renamed to
     my_structXYZ in an equivalent header in sys/compat/sys.  After that, you
     can freely modify my_struct as desired.

   Versioning the entry point
     The stub for the next version of the syscall will be __my_syscallXYZ(),
     and will have entry point sys___my_syscallXYZ().

   Modifying syscalls.conf
     sys/kern/syscalls.conf may need to be modified to contain compat_XYZ in
     the compatopts variable.

   Modifying syscalls.master
     First, add the next syscall to sys/kern/syscalls.master keeping
     my_syscall() as the name, and set the (optional) compat field of the
     declaration to XYZ.

     Next, modify the current version of the syscall, and replace the type
     field (usually just STD) with COMPAT_XYZ MODULAR compat_XYZ.

     The keyword MODULAR indicates that the system call can be part of a
     kernel module.  Even if the system call was not part of a module before,
     now it will be part of the COMPAT_XYZ module.

     Finally, if applicable, replace the types of the current and old versions
     of the syscall with the compat type.

     Overall, the final diff should look like

     - 123 STD                           { int|sys||my_syscall(struct my_struct *ms); }
     + 123 COMPAT_XYZ MODULAR compat_XYZ { int|sys||my_syscall(struct my_structXYZ *ms); }
     ...
     + 456 STD                           { int|sys|XYZ|my_syscall(struct my_struct *ms); }

   Modifying Makefile.rump
     If the current syscall is rump, sys/rump/Makefile.rump must contain XYZ
     in the RUMP_NBCOMPAT variable.

   Regenerating the system calls
     If versioning structs, then modify sys/kern/makesyscalls.sh by adding and
     entry for struct my_structXYZ type to uncompattypes.

     The uncompattypes map is used in rump(7) system call table generation, to
     map from the versioned types to the original names since rump(7) wants to
     have a non-versioned copy of the system call table.

     Then regenerate the syscall tables in the usual way, first by running
     sys/kern/makesyscalls.sh, then if the system call is rump, doing a build
     in sys/rump and then running sys/rump/makerumpsyscalls.sh passing it the
     path to the result of the build you just did as its first argument.

KERNEL COMPATIBILITY
     This section covers maintaining compatibility at the kernel level, by
     adding an entry point for the current syscall in an appropriate compat
     module.  For the purposes of this section, we assume the current syscall
     has entry point sys_my_syscall() and lives inside sys/kern/my_file.c.

   Creating the compat current syscall
     The compat version of the current syscall has entry point
     compat_XYZ_sys_my_syscall(), and should be implemented in
     sys/compat/common/my_file_XYZ.c with the same semantics as the current
     syscall.  Often this involves translating the arguments to the next
     syscall, and then calling that syscall's entry point.

   Adding it to the compat module
     sys/compat/common/my_file_XYZ.c must contain an array of struct
     syscall_package that declares the mapping between syscall number and
     entry point, terminating in a zero element (see sample diff below).

     Additionally, sys/compat/common/my_file_XYZ.c must contain two functions,
     my_file_XYZ_init() and my_file_XYZ_fini() that are used to
     initialize/clean up anything related to this syscall.  At the minimum
     they must make calls to syscall_establish() and syscall_disestablish()
     respectively, adding and removing the syscalls.  The stubs for these
     functions should be located in sys/compat/common/compat_mod.h.

     Overall, sys/compat/common/my_file_XYZ.c must at the minimum contain

           static const struct syscall_package my_file_XYZ_syscalls[] = {
                   { SYS_compat_XYZ_my_syscall, 0,
                       (sy_call_t *)compat_XYZ_sys_my_syscall },
                   { 0, 0, NULL },
           };

           int
           compat_XYZ_my_syscall(...)
           { /* Compat implementation goes here. */ }

           int
           my_file_XYZ_init(void)
           { return syscall_establish(NULL, my_file_XYZ_syscalls); }

           int
           my_file_XYZ_fini(void)
           { return syscall_disestablish(NULL, my_file_XYZ_syscalls); }

     Finally, sys/compat/common/compat_XYZ_mod.c needs to be modified to have
     its compat_XYZ_init() and compat_XYZ_fini() functions call
     my_file_XYZ_init() and my_file_XYZ_fini() respectively.

   Modifying old compat syscalls
     If the current syscall has already been versioned, you might need to
     modify the old compat syscalls in sys/compat/common to either use the
     next syscall or the current compat syscall.  Note that compat code can be
     made to depend on compat code for more recent releases.

USERLAND COMPATIBILITY
     With the exception of the libraries described below, making the rest of
     userland work will just involve recompiling, and perhaps changing a
     constant or a #define.

   libc
     A userland version of any old and current versions of the syscall must be
     implemented.  For the current syscall with stub
     my_syscall(struct my_struct *ms) in sys/sys/my_header.h, an
     implementation of my_syscall() must be written in
     lib/libc/compat/sys/compat_my_syscall.c.

     Additionally, a call to __warn_references() must be added in
     lib/libc/compat/sys/compat_my_syscall.c to warn of any uses of the compat
     syscall and mention how to use the next version of the syscall.  In
     almost all cases the instructions on how to use the next version of the
     syscall will be "include <sys/my_header.h> to generate correct
     reference".

     Overall, lib/libc/compat/sys/compat_my_syscall.c must at the minimum
     include

           #include <sys/compat/my_header.h>

           __warn_references(my_syscall,
               "warning: reference to compatibility my_syscall();"
               " message on how to use the next my_syscall()");

           int
           my_syscall()
           { /* Compat implementation goes here. */ }

NetBSD 10.99                     June 23, 2023                    NetBSD 10.99