mirror of
https://github.com/radareorg/r2sleigh.git
synced 2026-11-07 06:36:00 -10:00
103 lines
3.9 KiB
Python
Executable File
103 lines
3.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Generate r2abi's Linux syscall tables from the kernel's own UAPI headers.
|
|
|
|
Each table is what the C preprocessor makes of the header a program for that
|
|
architecture includes, with the feature macros that architecture's own
|
|
`asm/unistd.h` defines before it includes the generic list. Nothing is typed
|
|
in by hand, so a refresh is a re-run against newer headers:
|
|
|
|
scripts/gen_syscalls.py crates/r2abi/data
|
|
|
|
radare2 keeps its tables as hand-maintained `sdb` text; its arm-32 table is
|
|
the obsolete OABI numbering, and every one of them states an interrupt vector
|
|
the x86-64 `syscall` instruction does not have. These are the kernel's.
|
|
"""
|
|
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
INCLUDE = Path("/usr/include")
|
|
|
|
# (table, header, defines): the defines are what the architecture's own
|
|
# uapi asm/unistd.h sets before including asm-generic/unistd.h.
|
|
TABLES = [
|
|
("linux-x86-64", "x86_64-linux-gnu/asm/unistd_64.h", []),
|
|
("linux-x86-32", "x86_64-linux-gnu/asm/unistd_32.h", []),
|
|
(
|
|
"linux-arm-64",
|
|
"asm-generic/unistd.h",
|
|
[
|
|
"__BITS_PER_LONG=64",
|
|
"__ARCH_WANT_RENAMEAT",
|
|
"__ARCH_WANT_NEW_STAT",
|
|
"__ARCH_WANT_SET_GET_RLIMIT",
|
|
"__ARCH_WANT_TIME32_SYSCALLS",
|
|
"__ARCH_WANT_SYS_CLONE3",
|
|
"__ARCH_WANT_MEMFD_SECRET",
|
|
],
|
|
),
|
|
]
|
|
|
|
NAME = re.compile(r"^#define __NR_([A-Za-z0-9_]+)\s")
|
|
|
|
|
|
def names(header: Path, defines: list[str]) -> list[str]:
|
|
macros = subprocess.run(
|
|
["cpp", "-dM", *[f"-D{d}" for d in defines], str(header)],
|
|
check=True, capture_output=True, text=True,
|
|
).stdout
|
|
return sorted({m.group(1) for line in macros.splitlines() if (m := NAME.match(line))})
|
|
|
|
|
|
def numbers(header: Path, defines: list[str], wanted: list[str]) -> dict[str, int]:
|
|
"""Each name's number, as the preprocessor expands its macro."""
|
|
probe = "\n".join(f"@{name}@ __NR_{name}" for name in wanted)
|
|
source = f"#include \"{header}\"\n{probe}\n"
|
|
expanded = subprocess.run(
|
|
["cpp", "-P", *[f"-D{d}" for d in defines], "-"],
|
|
input=source, check=True, capture_output=True, text=True,
|
|
).stdout
|
|
out = {}
|
|
for line in expanded.splitlines():
|
|
match = re.match(r"^@(\w+)@\s+(.*)$", line.strip())
|
|
if not match:
|
|
continue
|
|
expression = match.group(2)
|
|
# Only integer arithmetic survives expansion; anything else is a name
|
|
# the header leaves undefined here, and is not a syscall of this table.
|
|
if not re.fullmatch(r"[0-9xa-fA-F+\-() ]+", expression):
|
|
continue
|
|
out[match.group(1)] = int(eval(expression, {"__builtins__": {}})) # noqa: S307
|
|
return out
|
|
|
|
|
|
def main() -> int:
|
|
destination = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("crates/r2abi/data")
|
|
version = subprocess.run(
|
|
["dpkg-query", "-W", "-f", "${Version}", "linux-libc-dev"],
|
|
capture_output=True, text=True,
|
|
).stdout.strip() or "unknown"
|
|
for table, header, defines in TABLES:
|
|
path = INCLUDE / header
|
|
found = numbers(path, defines, names(path, defines))
|
|
# Bookkeeping macros, not calls: the generic list's bounds and the
|
|
# first number an architecture may give its own calls.
|
|
for bound in ("syscalls", "arch_specific_syscall"):
|
|
found.pop(bound, None)
|
|
lines = [
|
|
f"# Linux syscall numbers for {table}, from the kernel UAPI header",
|
|
f"# {header} (linux-libc-dev {version}), expanded by the C preprocessor",
|
|
f"# with {', '.join(defines) or 'no feature macros'}.",
|
|
"# Generated by scripts/gen_syscalls.py; do not edit.",
|
|
]
|
|
lines += [f"{number} {name}" for name, number in sorted(found.items(), key=lambda kv: (kv[1], kv[0]))]
|
|
(destination / f"syscalls-{table}.txt").write_text("\n".join(lines) + "\n")
|
|
print(f"{table}: {len(found)} syscalls")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|