diff --git a/Scripts/GenerateSyscallNumbers.py b/Scripts/GenerateSyscallNumbers.py new file mode 100755 index 000000000..80a380a0a --- /dev/null +++ b/Scripts/GenerateSyscallNumbers.py @@ -0,0 +1,249 @@ +#!/usr/bin/python3 +from dataclasses import dataclass, field +import math +import sys +import logging +logger = logging.getLogger() +logger.setLevel(logging.WARNING) + +@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" + +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: + 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 = "" + else: + EntryName = split_text[3] + + 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_"): + # This line is defining a syscall for us + # eg: #define __NR_io_setup 0 + line = line.removeprefix("#define __NR_") + 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(__NR_"): + BeginsString = "__SYSCALL(__NR_" + elif line.startswith("__SC_COMP(__NR_"): + BeginsString = "__SC_COMP(__NR_" + elif line.startswith("__SC_3264(__NR_"): + BeginsString = "__SC_3264(__NR_" + elif line.startswith("__SC_COMP_3264(__NR_"): + BeginsString = "__SC_COMP_3264(__NR_" + else: + continue + + line = line.removeprefix(BeginsString) + split_text = line.split(",") + + Name = split_text[0] + Num = SyscallNumbers[Name] + ABI = Arch + EntryName = split_text[1].strip().split(")")[0] + + 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 == "": + 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 == "": + 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 " % (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())