FEX/Scripts/GenerateSyscallNumbers.py
Ryan Houdek 8ba0312d40 Syscalls: Prefix _ to shm syscall names to avoid namespace conflict
Termux uses defines for these, so our token pasting fails, but we also
still want to use their define so we can fall down their emulation
library whenever possible.

Prefix an underscore to be able to use both our number definitions and
their defines in the same file.
2022-09-05 02:39:42 -07:00

290 lines
8.6 KiB
Python
Executable File

#!/usr/bin/python3
from dataclasses import dataclass, field
import math
import sys
import logging
logger = logging.getLogger()
logger.setLevel(logging.WARNING)
# Usage of this script is `Scripts/GenerateSyscallNumbers.py <Path to Linux directory>`
# This will then parse the syscall headers and format them in an enum
# Then this will be output in stdout
# This output should then be checked and copied to the following headers, splitting up the enums:
# - Source/Tests/LinuxSyscalls/x32/SyscallsEnum.h
# - Source/Tests/LinuxSyscalls/x64/SyscallsEnum.h
# - Source/Tests/LinuxSyscalls/Arm64/SyscallsEnum.h
# `FEX_Syscalls_Common` is provided in the output as just an indicator for which syscalls are using the common
# syscall interface.
@dataclass
class SyscallDefinition:
arch: str
syscall_number: int
abi: str
name: str
entry: str
def __init__(self, Arch, SyscallNumber, ABI, Name, Entry):
self.arch = Arch
self.syscall_number = SyscallNumber
self.abi = ABI
self.name = Name
self.entry = Entry
@property
def Arch(self):
return self.arch
@property
def Number(self):
return self.syscall_number
@property
def ABI(self):
return self.abi
@property
def Name(self):
return self.name
@property
def EntryName(self):
return self.entry
Syscallx64File = "/arch/x86/entry/syscalls/syscall_64.tbl"
Syscallx86File = "/arch/x86/entry/syscalls/syscall_32.tbl"
SyscallArm64File = "/include/uapi/asm-generic/unistd.h"
# Syscall names that had naming conflict with some global definitions
# Renamed to work around that issue
DefinitionRenameDict = {
"pread64": "pread_64",
"pwrite64": "pwrite_64",
"prlimit64": "prlimit_64",
# Shm symbols conflict with termux defines and FEX's syscall token pasting.
# Underscore at the start to avoid name collision
"shmget": "_shmget",
"shmctl": "_shmctl",
"shmat": "_shmat",
"shmdt": "_shmdt",
}
Definitions_x64 = []
Definitions_x64_dict = {}
Definitions_x86 = []
Definitions_x86_dict = {}
Definitions_Arm64 = []
Definitions_Arm64_dict = {}
NumArches = 0
SyscallDefinitions = {}
def ParseArchSyscalls(Defs, DefsDict, Arch, FilePath, IgnoreArch):
global NumArches
global SyscallDefinitions
syscall_file = open(FilePath, "r")
text_lines = syscall_file.readlines()
syscall_file.close()
NumArches += 1
for line in text_lines:
line = line.strip()
# Skip lines that are a comment
if line.startswith("#") or len(line) == 0:
continue
# Format: <Number> <ABI> <Name> <Entry Name>
split_text = line.split()
Num = split_text[0]
ABI = split_text[1]
# If the ABI is on the ignore list then don't store it
if ABI in IgnoreArch:
continue
Name = split_text[2]
if (len(split_text) < 4):
# This sometimes happens if the host doesn't have the entry
EntryName = "<None>"
else:
EntryName = split_text[3]
if Name in DefinitionRenameDict:
Name = DefinitionRenameDict[Name]
Def = SyscallDefinition(Arch, Num, ABI, Name, EntryName)
Defs.append(Def)
if not Name in SyscallDefinitions:
SyscallDefinitions[Name] = []
SyscallDefinitions[Name].append(Def)
def ParseCommonArchSyscalls(Defs, DefsDict, Arch, FilePath):
global NumArches
global SyscallDefinitions
syscall_file = open(FilePath, "r")
text_lines = syscall_file.readlines()
syscall_file.close()
NumArches += 1
SyscallNumbers = {}
for line in text_lines:
line = line.strip()
if len(line) == 0:
continue
# Check for NR defines
if (line.startswith("#define __NR_") or
line.startswith("#define __NR3264_")):
# This line is defining a syscall for us
# eg: #define __NR_io_setup 0
line = line.removeprefix("#define __NR_")
line = line.removeprefix("#define __NR3264_")
split_text = line.split(" ")
# Store this for later
Name = split_text[0]
# Need to do len here since some lines are multiple spaces between define name and value
SyscallNumbers[Name] = split_text[len(split_text) - 1]
continue
BeginsString = ""
# Check for __SC_COMP and __SYSCALL defines
if line.startswith("__SYSCALL("):
BeginsString = "__SYSCALL("
elif line.startswith("__SC_COMP("):
BeginsString = "__SC_COMP("
elif line.startswith("__SC_3264("):
BeginsString = "__SC_3264("
elif line.startswith("__SC_COMP_3264("):
BeginsString = "__SC_COMP_3264("
else:
continue
line = line.removeprefix(BeginsString)
if line.startswith("__NR_"):
BeginsString = "__NR_"
elif line.startswith("__NR3264_"):
BeginsString = "__NR3264_"
line = line.removeprefix(BeginsString)
split_text = line.split(",")
Name = split_text[0]
Num = SyscallNumbers[Name]
ABI = Arch
EntryName = split_text[1].strip().split(")")[0]
if Name in DefinitionRenameDict:
Name = DefinitionRenameDict[Name]
Def = SyscallDefinition(Arch, Num, ABI, Name, EntryName)
Defs.append(Def)
if not Name in SyscallDefinitions:
SyscallDefinitions[Name] = []
SyscallDefinitions[Name].append(Def)
def ExportSyscallDefines(Defs, DefsDict, Arch, UnsupportedDefs):
AlreadyExported = []
print("enum Syscalls_{} {{".format(Arch))
for Def in Defs:
if Def.EntryName == "<None>":
print(" // No entrypoint. -ENOSYS")
print(" SYSCALL_{}_{} = {},".format(Arch, Def.Name, Def.Number))
AlreadyExported.append(Def.Name)
# Print ourselves a max
Max = 1 << (int(math.log(len(Defs), 2)) + 1)
print(" SYSCALL_{}_MAX = {},".format(Arch, Max))
if len(UnsupportedDefs) != 0:
# Print out syscalls that don't exist on this architecture
print("")
print(" // Unsupported syscalls on this host")
for DefList in UnsupportedDefs:
for Def in DefList:
# If the syscall name exists in the full definition dictionary
# but DOESN'T exist in our current arch AND exists in the Unsupported dicts
# Then we need to export it as an unnamed syscall entry
if Def.Name in AlreadyExported:
continue
print(" SYSCALL_{}_{} = ~0,".format(Arch, Def.Name))
AlreadyExported.append(Def.name)
print("};")
def ExportCommonSyscallDefines():
global Definitions_Arm64
global SyscallDefinitions
print("enum FEX_Syscalls_Common {")
for Def in Definitions_Arm64:
# Check the dict to ensure the definitions exist everywhere
if not Def.Name in SyscallDefinitions:
continue
Defs = SyscallDefinitions[Def.Name]
if len(Defs) != NumArches:
continue
Number = Def.Number
Matches = True
for AllDef in Defs:
if AllDef.Number != Def.Number:
Matches = False
if not Matches:
continue
for AllDef in Defs:
if AllDef.EntryName == "<None>":
print(" // {} No entrypoint. -ENOSYS".format(AllDef.Arch))
print(" SYS_{} = {},".format(Def.Name, Def.Number))
# Find a max between all architectures
Maximums = []
for Defs in [Definitions_x64, Definitions_x86, Definitions_Arm64]:
Maximums.append(1 << (int(math.log(len(Defs), 2)) + 1))
print(" SYSCALL_MAX = {},".format(max(Maximums)))
print("};")
def main():
if sys.version_info[0] < 3:
logging.critical ("Python 3 or a more recent version is required.")
if (len(sys.argv) < 2):
print ("usage: %s <Linux git tree>" % (sys.argv[0]))
LinuxPath = sys.argv[1]
ParseArchSyscalls(Definitions_x86, Definitions_x86_dict, "x86", LinuxPath + Syscallx86File, [])
ParseArchSyscalls(Definitions_x64, Definitions_x64_dict, "x64", LinuxPath + Syscallx64File, ["x32"])
ParseCommonArchSyscalls(Definitions_Arm64, Definitions_Arm64_dict, "Arm64", LinuxPath + SyscallArm64File)
ExportSyscallDefines(Definitions_x86, Definitions_x86_dict, "x86",[])
ExportSyscallDefines(Definitions_x64, Definitions_x64_dict, "x64", [Definitions_x86])
ExportSyscallDefines(Definitions_Arm64, Definitions_Arm64_dict, "Arm64", [Definitions_x86, Definitions_x64])
ExportCommonSyscallDefines()
if __name__ == "__main__":
# execute only if run as a script
sys.exit(main())