1#!/usr/bin/env python3
2# SPDX-License-Identifier: LGPL-2.1-or-later
3
4import sys
5import functools
6
7# We only generate numbers for a dozen or so syscalls
8SYSCALLS = [
9    'bpf',
10    'close_range',
11    'copy_file_range',
12    'epoll_pwait2',
13    'getrandom',
14    'memfd_create',
15    'mount_setattr',
16    'move_mount',
17    'name_to_handle_at',
18    'open_tree',
19    'openat2',
20    'pidfd_open',
21    'pidfd_send_signal',
22    'pkey_mprotect',
23    'renameat2',
24    'setns',
25    'statx',
26]
27
28def dictify(f):
29    def wrap(*args, **kwargs):
30        return dict(f(*args, **kwargs))
31    return functools.update_wrapper(wrap, f)
32
33@dictify
34def parse_syscall_table(filename):
35    print(f'Reading {filename}…')
36    for line in open(filename):
37        items = line.split()
38        if len(items) >= 2:
39            yield items[0], int(items[1])
40
41def parse_syscall_tables(filenames):
42    return {filename.split('-')[-1][:-4]: parse_syscall_table(filename)
43            for filename in filenames}
44
45DEF_TEMPLATE_A = '''\
46
47#ifndef __IGNORE_{syscall}
48'''
49
50DEF_TEMPLATE_B = '''\
51#  if defined(__aarch64__)
52#    define systemd_NR_{syscall} {nr_arm64}
53#  elif defined(__alpha__)
54#    define systemd_NR_{syscall} {nr_alpha}
55#  elif defined(__arc__) || defined(__tilegx__)
56#    define systemd_NR_{syscall} {nr_arc}
57#  elif defined(__arm__)
58#    define systemd_NR_{syscall} {nr_arm}
59#  elif defined(__i386__)
60#    define systemd_NR_{syscall} {nr_i386}
61#  elif defined(__ia64__)
62#    define systemd_NR_{syscall} {nr_ia64}
63#  elif defined(__loongarch64)
64#    define systemd_NR_{syscall} {nr_loongarch64}
65#  elif defined(__m68k__)
66#    define systemd_NR_{syscall} {nr_m68k}
67#  elif defined(_MIPS_SIM)
68#    if _MIPS_SIM == _MIPS_SIM_ABI32
69#      define systemd_NR_{syscall} {nr_mipso32}
70#    elif _MIPS_SIM == _MIPS_SIM_NABI32
71#      define systemd_NR_{syscall} {nr_mips64n32}
72#    elif _MIPS_SIM == _MIPS_SIM_ABI64
73#      define systemd_NR_{syscall} {nr_mips64}
74#    else
75#      error "Unknown MIPS ABI"
76#    endif
77#  elif defined(__powerpc__)
78#    define systemd_NR_{syscall} {nr_powerpc}
79#  elif defined(__riscv)
80#    if __riscv_xlen == 32
81#      define systemd_NR_{syscall} {nr_riscv32}
82#    elif __riscv_xlen == 64
83#      define systemd_NR_{syscall} {nr_riscv64}
84#    else
85#      error "Unknown RISC-V ABI"
86#    endif
87#  elif defined(__s390__)
88#    define systemd_NR_{syscall} {nr_s390}
89#  elif defined(__sparc__)
90#    define systemd_NR_{syscall} {nr_sparc}
91#  elif defined(__x86_64__)
92#    if defined(__ILP32__)
93#      define systemd_NR_{syscall} ({nr_x86_64} | /* __X32_SYSCALL_BIT */ 0x40000000)
94#    else
95#      define systemd_NR_{syscall} {nr_x86_64}
96#    endif
97#  elif !defined(missing_arch_template)
98%s
99#  endif
100'''
101
102DEF_TEMPLATE_C = '''\
103
104/* may be an (invalid) negative number due to libseccomp, see PR 13319 */
105#  if defined __NR_{syscall} && __NR_{syscall} >= 0
106#    if defined systemd_NR_{syscall}
107assert_cc(__NR_{syscall} == systemd_NR_{syscall});
108#    endif
109#  else
110#    if defined __NR_{syscall}
111#      undef __NR_{syscall}
112#    endif
113#    if defined systemd_NR_{syscall} && systemd_NR_{syscall} >= 0
114#      define __NR_{syscall} systemd_NR_{syscall}
115#    endif
116#  endif
117#endif'''
118
119DEF_TEMPLATE = (DEF_TEMPLATE_A +
120                DEF_TEMPLATE_B % '#    warning "{syscall}() syscall number is unknown for your architecture"' +
121                DEF_TEMPLATE_C)
122
123ARCH_CHECK = '''\
124/* Note: if this code looks strange, this is because it is derived from the same
125 * template as the per-syscall blocks below. */
126''' + '\n'.join(line for line in DEF_TEMPLATE_B.splitlines()
127                       if ' define ' not in line) % '''\
128#    warning "Current architecture is missing from the template"
129#    define missing_arch_template 1'''
130
131def print_syscall_def(syscall, tables, out):
132    mappings = {f'nr_{arch}':t.get(syscall, -1)
133                for arch, t in tables.items()}
134    print(DEF_TEMPLATE.format(syscall=syscall, **mappings),
135          file=out)
136
137def print_syscall_defs(syscalls, tables, out):
138    print('''\
139/* SPDX-License-Identifier: LGPL-2.1-or-later
140 * This file is generated by src/basic/missing_syscalls.py. Do not edit!
141 *
142 * Use 'ninja -C build update-syscall-tables' to download new syscall tables,
143 * and 'ninja -C build update-syscall-header' to regenerate this file.
144 */
145#pragma once
146''',
147          file=out)
148    print(ARCH_CHECK, file=out)
149    for syscall in syscalls:
150        print_syscall_def(syscall, tables, out)
151
152if __name__ == '__main__':
153    output_file = sys.argv[1]
154    arch_files = sys.argv[2:]
155    out = open(output_file, 'wt')
156
157    tables = parse_syscall_tables(arch_files)
158    print_syscall_defs(SYSCALLS, tables, out)
159
160    print(f'Wrote {output_file}')
161