2 # Helpers for glibc system call list processing.
3 # Copyright (C) 2018-2024 Free Software Foundation, Inc.
4 # This file is part of the GNU C Library.
6 # The GNU C Library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # The GNU C Library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with the GNU C Library; if not, see
18 # <http://www.gnu.org/licenses/>.
23 if __name__
!= '__main__':
24 # When called as a main program, this is not needed.
27 def extract_system_call_name(macro
):
28 """Convert the macro name (with __NR_) to a system call name."""
30 if macro
.startswith(prefix
):
31 return macro
[len(prefix
):]
33 raise ValueError('invalid system call name: {!r}'.format(macro
))
35 # Matches macros for system call names.
36 RE_SYSCALL
= re
.compile('__NR_.*')
38 # Some __NR_ constants are not real
39 RE_PSEUDO_SYSCALL
= re
.compile(r
"""__NR_(
40 # Reserved system call.
41 (unused|reserved)[0-9]+
43 # Pseudo-system call which describes a range.
44 |(syscalls|arch_specific_syscall|(OABI_)?SYSCALL_BASE|SYSCALL_MASK)
45 |(|64_|[NO]32_)Linux(_syscalls)?
48 def kernel_constants(cc
):
49 """Return a dictionary with the kernel-defined system call numbers.
51 This comes from <asm/unistd.h>.
54 return {extract_system_call_name(name
) : int(value
)
55 for name
, value
in glibcextract
.compute_macro_consts(
56 '#include <asm/unistd.h>\n'
57 # Regularize the kernel definitions if necessary.
58 '#include <fixup-asm-unistd.h>',
59 cc
, macro_re
=RE_SYSCALL
, exclude_re
=RE_PSEUDO_SYSCALL
)
62 class SyscallNamesList
:
63 """The list of known system call names.
65 glibc keeps a list of system call names. The <sys/syscall.h>
66 header needs to provide a SYS_ name for each __NR_ macro,
67 and the generated <bits/syscall.h> header uses an
68 architecture-independent list, so that there is a chance that
69 system calls arriving late on certain architectures will automatically
70 get the expected SYS_ macro.
72 syscalls: list of strings with system call names
73 kernel_version: tuple of integers; the kernel version given in the file
76 def __init__(self
, lines
):
79 self
.kernel_version
= None
80 self
.__lines
= tuple(lines
)
81 for line
in self
.__lines
:
83 if (not line
) or line
[0] == '#':
87 self
.syscalls
.append(comps
[0])
88 if old_name
is not None:
89 if comps
[0] < old_name
:
91 'name list is not sorted: {!r} < {!r}'.format(
95 if len(comps
) == 2 and comps
[0] == "kernel":
96 if self
.kernel_version
is not None:
98 "multiple kernel versions: {!r} and !{r}".format(
99 kernel_version
, comps
[1]))
100 self
.kernel_version
= tuple(map(int, comps
[1].split(".")))
102 raise ValueError("invalid line: !r".format(line
))
103 if self
.kernel_version
is None:
104 raise ValueError("missing kernel version")
106 def merge(self
, names
):
107 """Merge sequence NAMES and return the lines of the new file."""
108 names
= list(set(names
) - set(self
.syscalls
))
113 result
.append(names
[-1] + "\n")
116 for line
in self
.__lines
:
117 comps
= line
.strip().split()
118 if len(comps
) == 1 and not comps
[0].startswith("#"):
119 # File has a system call at this position. Insert all
120 # the names that come before the name in the file
122 while names
and names
[-1] < comps
[0]:
130 def load_arch_syscall_header(path
):
131 """"Load the system call header form the file PATH.
133 The file must consist of lines of this form:
137 The file is parsed verbatim, without running it through a C
138 preprocessor or parser. The intent is that the file can be
139 readily processed by tools.
142 with
open(path
) as inp
:
148 # Ignore the initial comment line.
149 if line
.startswith("/*") and line
.endswith("*/"):
152 define
, name
, number
= line
.split(' ', 2)
153 if define
!= '#define':
154 raise ValueError("invalid syscall header line: {!r}".format(
156 result
[extract_system_call_name(name
)] = int(number
)
159 if old_name
is not None:
162 'system call list is not sorted: {!r} < {!r}'.format(
167 def linux_kernel_version(cc
):
168 """Return the (major, minor) version of the Linux kernel headers."""
169 sym_data
= ['#include <linux/version.h>', 'START',
170 ('LINUX_VERSION_CODE', 'LINUX_VERSION_CODE')]
171 val
= glibcextract
.compute_c_consts(sym_data
, cc
)['LINUX_VERSION_CODE']
173 return ((val
& 0xff0000) >> 16, (val
& 0xff00) >> 8)
176 """Canonical name and location of a syscall header."""
178 def __init__(self
, name
, path
):
183 return 'ArchSyscall(name={!r}, patch={!r})'.format(
184 self
.name
, self
.path
)
186 def list_arch_syscall_headers(topdir
):
187 """A generator which returns all the ArchSyscall objects in a tree."""
189 sysdeps
= os
.path
.join(topdir
, 'sysdeps', 'unix', 'sysv', 'linux')
190 for root
, dirs
, files
in os
.walk(sysdeps
):
192 for filename
in files
:
193 if filename
== 'arch-syscall.h':
195 name
=os
.path
.relpath(root
, sysdeps
),
196 path
=os
.path
.join(root
, filename
))
199 """Entry point when called as the main program."""
204 # Top-level directory of the source tree.
205 topdir
= os
.path
.realpath(os
.path
.join(
206 os
.path
.dirname(os
.path
.realpath(__file__
)), *('..',) * 4))
209 parser
= argparse
.ArgumentParser(description
=__doc__
)
210 subparsers
= parser
.add_subparsers(dest
='command', required
=True)
211 subparsers
.add_parser('list-headers',
212 help='Print the absolute paths of all arch-syscall.h header files')
213 subparser
= subparsers
.add_parser('query-syscall',
214 help='Summarize the implementation status of system calls')
215 subparser
.add_argument('syscalls', help='Which syscalls to check',
218 parser
= get_parser()
219 args
= parser
.parse_args()
221 if args
.command
== 'list-headers':
222 for header
in sorted([syscall
.path
for syscall
223 in list_arch_syscall_headers(topdir
)]):
226 elif args
.command
== 'query-syscall':
227 # List of system call tables.
228 tables
= sorted(list_arch_syscall_headers(topdir
),
229 key
=lambda syscall
: syscall
.name
)
231 table
.numbers
= load_arch_syscall_header(table
.path
)
233 for nr
in args
.syscalls
:
234 defined
= [table
.name
for table
in tables
235 if nr
in table
.numbers
]
236 undefined
= [table
.name
for table
in tables
237 if nr
not in table
.numbers
]
239 print('{}: not defined on any architecture'.format(nr
))
241 print('{}: defined on all architectures'.format(nr
))
243 print('{}:'.format(nr
))
244 print(' defined: {}'.format(' '.join(defined
)))
245 print(' undefined: {}'.format(' '.join(undefined
)))
248 # Unrecognized command.
251 if __name__
== '__main__':