Files
FEX-Emu--FEX/Scripts/GenerateSyscallNumbers.py
T
Ryan Houdek aae1dd4d81 Adds new script to generate syscall enums
Lets us define our own enums for syscall mapping.
Necessary to get all the syscall IDs in a sane way
2021-11-15 14:29:49 -08:00

250 lines
7.2 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)
@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: <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]
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 == "<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())