mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 15:00:17 +02:00
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
This commit is contained in:
1 parent
977d92b7bc
commit
aae1dd4d81
1 file changed
+249
Executable
+249
@@ -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: <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())
|
||||
Reference in new issue
Block a user