mirror of
https://github.com/FEX-Emu/FEX.git
synced 2024-12-12 16:46:23 +00:00
0aff3941f4
X11 has an attribute that was causing function declarations to be missed. These definitions exist in XLibint.h eg: ```cpp extern void _XEatData( Display* /* dpy */, unsigned long /* n */ ) _X_COLD; ``` This `_X_COLD` attribute was causing these function definitions to get missed.
553 lines
18 KiB
Python
Executable File
553 lines
18 KiB
Python
Executable File
#!/usr/bin/python3
|
|
import clang.cindex
|
|
from clang.cindex import CursorKind
|
|
from clang.cindex import TypeKind
|
|
from clang.cindex import TranslationUnit
|
|
import sys
|
|
from dataclasses import dataclass, field
|
|
import subprocess
|
|
import logging
|
|
logger = logging.getLogger()
|
|
logger.setLevel(logging.WARNING)
|
|
|
|
@dataclass
|
|
class TypeDefinition:
|
|
TYPE_UNKNOWN = 0
|
|
TYPE_STRUCT = 1
|
|
TYPE_UNION = 2
|
|
TYPE_FIELD = 3
|
|
TYPE_VARDECL = 4
|
|
|
|
name: str
|
|
type: int
|
|
def __init__(self, Name, Type):
|
|
self.name = Name
|
|
self.type = Type
|
|
|
|
@property
|
|
def Name(self):
|
|
return self.name
|
|
@property
|
|
def Type(self):
|
|
return self.type
|
|
|
|
@dataclass
|
|
class AliasType:
|
|
ALIAS_X86_32 = 0
|
|
ALIAS_X86_64 = 1
|
|
ALIAS_AARCH64 = 2
|
|
ALIAS_WIN32 = 3
|
|
ALIAS_WIN64 = 4
|
|
Name: str
|
|
AliasType: int
|
|
def __init__(self, Name, Type):
|
|
self.Name = Name
|
|
self.AliasType = Type
|
|
|
|
@dataclass
|
|
class StructDefinition(TypeDefinition):
|
|
Size: int
|
|
Aliases: list
|
|
Members: list
|
|
ExpectFEXMatch: bool
|
|
|
|
def __init__(self, Name, Size):
|
|
super(StructDefinition, self).__init__(Name, TypeDefinition.TYPE_STRUCT)
|
|
self.Size = Size
|
|
self.Aliases = []
|
|
self.Members = []
|
|
self.ExpectFEXMatch = False
|
|
|
|
@dataclass
|
|
class UnionDefinition(TypeDefinition):
|
|
Size: int
|
|
Aliases: list
|
|
Members: list
|
|
ExpectFEXMatch: bool
|
|
|
|
def __init__(self, Name, Size):
|
|
super(UnionDefinition, self).__init__(Name, TypeDefinition.TYPE_UNION)
|
|
self.Size = Size
|
|
self.Aliases = []
|
|
self.Members = []
|
|
self.ExpectFEXMatch = False
|
|
|
|
@dataclass
|
|
class FieldDefinition(TypeDefinition):
|
|
Size: int
|
|
OffsetOf: int
|
|
Alignment: int
|
|
def __init__(self, Name, Size, OffsetOf, Alignment):
|
|
super(FieldDefinition, self).__init__(Name, TypeDefinition.TYPE_FIELD)
|
|
self.Size = Size
|
|
self.OffsetOf = OffsetOf
|
|
self.Alignment = Alignment
|
|
|
|
@dataclass
|
|
class VarDeclDefinition(TypeDefinition):
|
|
Size: int
|
|
Aliases: list
|
|
ExpectFEXMatch: bool
|
|
Value: str
|
|
|
|
def __init__(self, Name, Size):
|
|
super(VarDeclDefinition, self).__init__(Name, TypeDefinition.TYPE_VARDECL)
|
|
self.Size = Size
|
|
self.Aliases = []
|
|
self.ExpectFEXMatch = False
|
|
|
|
@dataclass
|
|
class ArchDB:
|
|
Parsed: bool
|
|
ArchName: str
|
|
NamespaceScope: list
|
|
CurrentNamespace: str
|
|
TU: TranslationUnit
|
|
Structs: dict
|
|
Unions: dict
|
|
VarDecls: dict
|
|
FieldDecls: list
|
|
def __init__(self, ArchName):
|
|
self.Parsed = True
|
|
self.ArchName = ArchName
|
|
self.NamespaceScope = []
|
|
self.CurrentNamespace = ""
|
|
self.TU = None
|
|
self.Structs = {}
|
|
self.Unions = {}
|
|
self.VarDecls = {}
|
|
self.FieldDecls = []
|
|
|
|
@dataclass
|
|
class FunctionDecl:
|
|
Name: str
|
|
Ret: str
|
|
Params: list
|
|
|
|
def __init__(self, Name, Ret):
|
|
self.Name = Name
|
|
self.Ret = Ret
|
|
self.Params = []
|
|
|
|
FunctionDecls = []
|
|
|
|
def HandleFunctionDeclCursor(Arch, Cursor):
|
|
if (Cursor.is_definition()):
|
|
return Arch
|
|
|
|
#logging.critical ("Unhandled FunctionDeclCursor {0}-{1}-{2}-{3}".format(Cursor.kind, Cursor.type.spelling, Cursor.spelling,
|
|
# Cursor.result_type.spelling))
|
|
|
|
Function = FunctionDecl(Cursor.spelling, Cursor.result_type.spelling)
|
|
|
|
for Child in Cursor.get_children():
|
|
if (Child.kind == CursorKind.TYPE_REF):
|
|
# This will give us the return type
|
|
# We skip this since we get it at the start instead
|
|
pass
|
|
elif (Child.kind == CursorKind.PARM_DECL):
|
|
# This gives us a parameter type
|
|
Function.Params.append(Child.type.spelling)
|
|
elif (Child.kind == CursorKind.ASM_LABEL_ATTR):
|
|
# Whatever you are we don't care about you
|
|
return Arch
|
|
elif (Child.kind == CursorKind.WARN_UNUSED_RESULT_ATTR):
|
|
# Whatever you are we don't care about you
|
|
return Arch
|
|
elif (Child.kind == CursorKind.VISIBILITY_ATTR or
|
|
Child.kind == CursorKind.UNEXPOSED_ATTR or
|
|
Child.kind == CursorKind.CONST_ATTR or
|
|
Child.kind == CursorKind.PURE_ATTR):
|
|
pass
|
|
else:
|
|
logging.critical ("\tUnhandled FunctionDeclCursor {0}-{1}-{2}".format(Child.kind, Child.type.spelling, Child.spelling))
|
|
sys.exit(-1)
|
|
|
|
FunctionDecls.append(Function)
|
|
return Arch
|
|
|
|
def PrintFunctionDecls():
|
|
for Decl in FunctionDecls:
|
|
print("template<> struct fex_gen_config<{}> {{}};".format(Decl.Name))
|
|
|
|
def FindClangArguments(OriginalArguments):
|
|
AddedArguments = ["clang"]
|
|
AddedArguments.extend(OriginalArguments)
|
|
AddedArguments.extend(["-v", "-x", "c++", "-S", "-"])
|
|
Proc = subprocess.Popen(AddedArguments, stderr = subprocess.PIPE, stdin = subprocess.DEVNULL)
|
|
NewIncludes = []
|
|
BeginSearch = False
|
|
while True:
|
|
Line = Proc.stderr.readline().strip()
|
|
|
|
if not Line:
|
|
Proc.terminate()
|
|
break
|
|
|
|
if (Line == b"End of search list."):
|
|
BeginSearch = False
|
|
Proc.terminate()
|
|
break
|
|
|
|
if (BeginSearch == True):
|
|
NewIncludes.append("-I" + Line.decode('ascii'))
|
|
|
|
if (Line == b"#include <...> search starts here:"):
|
|
BeginSearch = True
|
|
|
|
# Add back original arguments
|
|
NewIncludes.extend(OriginalArguments)
|
|
return NewIncludes
|
|
|
|
def SetNamespace(Arch):
|
|
Arch.CurrentNamespace = ""
|
|
for Namespace in Arch.NamespaceScope:
|
|
Arch.CurrentNamespace = Arch.CurrentNamespace + Namespace + "::"
|
|
|
|
def HandleStructDeclCursor(Arch, Cursor, NameOverride = ""):
|
|
# Append namespace
|
|
CursorName = ""
|
|
StructType = Cursor.type
|
|
if (len(StructType.spelling) == 0):
|
|
CursorName = NameOverride
|
|
else:
|
|
CursorName = StructType.spelling
|
|
|
|
if (len(CursorName) != 0):
|
|
Arch.NamespaceScope.append(CursorName)
|
|
SetNamespace(Arch)
|
|
|
|
Struct = StructDefinition(
|
|
Name = CursorName,
|
|
Size = StructType.get_size())
|
|
|
|
# Handle children
|
|
Arch.Structs[Struct.Name] = HandleStructElements(Arch, Struct, Cursor)
|
|
|
|
# Pop namespace off
|
|
if (len(CursorName) != 0):
|
|
Arch.NamespaceScope.pop()
|
|
SetNamespace(Arch)
|
|
|
|
return Arch
|
|
|
|
def HandleUnionDeclCursor(Arch, Cursor, NameOverride = ""):
|
|
# Append namespace
|
|
CursorName = ""
|
|
|
|
if (len(Cursor.spelling) == 0):
|
|
CursorName = NameOverride
|
|
else:
|
|
CursorName = Cursor.spelling
|
|
|
|
if (len(CursorName) != 0):
|
|
Arch.NamespaceScope.append(CursorName)
|
|
SetNamespace(Arch)
|
|
|
|
UnionType = Cursor.type
|
|
Union = UnionDefinition(
|
|
Name = CursorName,
|
|
Size = UnionType.get_size())
|
|
Arch.Unions[Union.Name] = Union
|
|
|
|
# Handle children
|
|
Arch.Unions[Union.Name] = HandleStructElements(Arch, Union, Cursor)
|
|
|
|
# Pop namespace off
|
|
if (len(CursorName) != 0):
|
|
Arch.NamespaceScope.pop()
|
|
SetNamespace(Arch)
|
|
|
|
return Arch
|
|
|
|
def HandleVarDeclCursor(Arch, Cursor):
|
|
CursorName = Cursor.spelling
|
|
DeclType = Cursor.type
|
|
Def = Cursor.get_definition()
|
|
|
|
VarDecl = VarDeclDefinition(
|
|
Name = CursorName,
|
|
Size = DeclType.get_size())
|
|
Arch.VarDecls[VarDecl.Name] = HandleVarDeclElements(Arch, VarDecl, Cursor)
|
|
return Arch
|
|
|
|
def HandleVarDeclElements(Arch, VarDecl, Cursor):
|
|
for Child in Cursor.get_children():
|
|
|
|
if (Child.kind == CursorKind.ANNOTATE_ATTR):
|
|
if (Child.spelling.startswith("ioctl-alias-")):
|
|
Sections = Child.spelling.split("-")
|
|
if (Sections[2] == "x86_32"):
|
|
VarDecl.Aliases.append(AliasType(Sections[3], AliasType.ALIAS_X86_32))
|
|
elif (Sections[2] == "x86_64"):
|
|
VarDecl.Aliases.append(AliasType(Sections[3], AliasType.ALIAS_X86_64))
|
|
elif (Sections[2] == "aarch64"):
|
|
VarDecl.Aliases.append(AliasType(Sections[3], AliasType.ALIAS_AARCH64))
|
|
elif (Sections[2] == "win32"):
|
|
VarDecl.Aliases.append(AliasType(Sections[3], AliasType.ALIAS_WIN32))
|
|
elif (Sections[2] == "win64"):
|
|
VarDecl.Aliases.append(AliasType(Sections[3], AliasType.ALIAS_WIN64))
|
|
else:
|
|
logging.critical ("Can't handle alias type '{0}'".format(Child.spelling))
|
|
Arch.Parsed = False
|
|
elif (Child.spelling == "fex-match"):
|
|
VarDecl.ExpectedFEXMatch = True
|
|
else:
|
|
# Unknown annotation
|
|
pass
|
|
elif (Child.kind == CursorKind.TYPE_REF or
|
|
Child.kind == CursorKind.UNEXPOSED_EXPR or
|
|
Child.kind == CursorKind.PAREN_EXPR or
|
|
Child.kind == CursorKind.BINARY_OPERATOR
|
|
):
|
|
pass
|
|
|
|
return VarDecl
|
|
|
|
def HandleTypeDefDeclCursor(Arch, Cursor):
|
|
TypeDefType = Cursor.underlying_typedef_type
|
|
CanonicalType = TypeDefType.get_canonical()
|
|
|
|
TypeDefName = Cursor.type.get_typedef_name()
|
|
|
|
if (TypeDefType.kind == TypeKind.ELABORATED and CanonicalType.kind == TypeKind.RECORD):
|
|
if (len(TypeDefName) != 0):
|
|
HandleTypeDefDecl(Arch, Cursor, TypeDefName)
|
|
|
|
# Append namespace
|
|
Arch.NamespaceScope.append(TypeDefName)
|
|
SetNamespace(Arch)
|
|
|
|
Arch = HandleCursor(Arch, Cursor)
|
|
#StructType = Cursor.type
|
|
#Struct = StructDefinition(
|
|
# Name = TypeDefName,
|
|
# Size = CanonicalType.get_size())
|
|
#Arch.Structs[TypeDefName] = Struct
|
|
|
|
## Handle children
|
|
#Arch.Structs[TypeDefName] = HandleStructElements(Arch, Struct, Cursor)
|
|
|
|
# Pop namespace off
|
|
Arch.NamespaceScope.pop()
|
|
SetNamespace(Arch)
|
|
else:
|
|
if (len(TypeDefName) != 0):
|
|
Def = Cursor.get_definition()
|
|
|
|
VarDecl = VarDeclDefinition(
|
|
Name = TypeDefName,
|
|
Size = CanonicalType.get_size())
|
|
Arch.VarDecls[VarDecl.Name] = HandleVarDeclElements(Arch, VarDecl, Cursor)
|
|
|
|
return Arch
|
|
|
|
def HandleStructElements(Arch, Struct, Cursor):
|
|
for Child in Cursor.get_children():
|
|
# logging.info ("\t\tStruct/Union Children: Cursor \"{0}{1}\" of kind {2}".format(Arch.CurrentNamespace, Child.spelling, Child.kind))
|
|
if (Child.kind == CursorKind.ANNOTATE_ATTR):
|
|
if (Child.spelling.startswith("alias-")):
|
|
Sections = Child.spelling.split("-")
|
|
if (Sections[1] == "x86_32"):
|
|
Struct.Aliases.append(AliasType(Sections[2], AliasType.ALIAS_X86_32))
|
|
elif (Sections[1] == "x86_64"):
|
|
Struct.Aliases.append(AliasType(Sections[2], AliasType.ALIAS_X86_64))
|
|
elif (Sections[1] == "aarch64"):
|
|
Struct.Aliases.append(AliasType(Sections[2], AliasType.ALIAS_AARCH64))
|
|
elif (Sections[1] == "win32"):
|
|
Struct.Aliases.append(AliasType(Sections[2], AliasType.ALIAS_WIN32))
|
|
elif (Sections[1] == "win64"):
|
|
Struct.Aliases.append(AliasType(Sections[2], AliasType.ALIAS_WIN64))
|
|
else:
|
|
logging.critical ("Can't handle alias type '{0}'".format(Child.spelling))
|
|
Arch.Parsed = False
|
|
|
|
elif (Child.spelling == "fex-match"):
|
|
Struct.ExpectedFEXMatch = True
|
|
else:
|
|
# Unknown annotation
|
|
pass
|
|
elif (Child.kind == CursorKind.FIELD_DECL):
|
|
ParentType = Cursor.type
|
|
FieldType = Child.type
|
|
Field = FieldDefinition(
|
|
Name = Child.spelling,
|
|
Size = FieldType.get_size(),
|
|
OffsetOf = ParentType.get_offset(Child.spelling),
|
|
Alignment = FieldType.get_align())
|
|
|
|
#logging.info ("\t{0}".format(Child.spelling))
|
|
#logging.info ("\t\tSize of type: {0}".format(FieldType.get_size()));
|
|
#logging.info ("\t\tAlignment of type: {0}".format(FieldType.get_align()));
|
|
#logging.info ("\t\tOffsetof of type: {0}".format(ParentType.get_offset(Child.spelling)));
|
|
Struct.Members.append(Field)
|
|
Arch.FieldDecls.append(Field)
|
|
elif (Child.kind == CursorKind.STRUCT_DECL):
|
|
ParentType = Cursor.type
|
|
FieldType = Child.type
|
|
Field = FieldDefinition(
|
|
Name = Child.spelling,
|
|
Size = FieldType.get_size(),
|
|
OffsetOf = ParentType.get_offset(Child.spelling),
|
|
Alignment = FieldType.get_align())
|
|
|
|
#logging.info ("\t{0}".format(Child.spelling))
|
|
#logging.info ("\t\tSize of type: {0}".format(FieldType.get_size()));
|
|
#logging.info ("\t\tAlignment of type: {0}".format(FieldType.get_align()));
|
|
#logging.info ("\t\tOffsetof of type: {0}".format(ParentType.get_offset(Child.spelling)));
|
|
Struct.Members.append(Field)
|
|
Arch.FieldDecls.append(Field)
|
|
Arch = HandleStructDeclCursor(Arch, Child)
|
|
elif (Child.kind == CursorKind.UNION_DECL):
|
|
Struct = HandleStructElements(Arch, Struct, Child)
|
|
#ParentType = Cursor.type
|
|
#FieldType = Child.type
|
|
#Field = FieldDefinition(
|
|
# Name = Child.spelling,
|
|
# Size = FieldType.get_size(),
|
|
# OffsetOf = ParentType.get_offset(Child.spelling),
|
|
# Alignment = FieldType.get_align())
|
|
|
|
#logging.info ("\t{0}".format(Child.spelling))
|
|
#logging.info ("\t\tSize of type: {0}".format(FieldType.get_size()));
|
|
#logging.info ("\t\tAlignment of type: {0}".format(FieldType.get_align()));
|
|
#logging.info ("\t\tOffsetof of type: {0}".format(ParentType.get_offset(Child.spelling)));
|
|
#Struct.Members.append(Field)
|
|
#Arch.FieldDecls.append(Field)
|
|
#Arch = HandleUnionDeclCursor(Arch, Child)
|
|
elif (Child.kind == CursorKind.TYPEDEF_DECL):
|
|
Arch = HandleTypeDefDeclCursor(Arch, Child)
|
|
else:
|
|
Arch = HandleCursor(Arch, Child)
|
|
|
|
return Struct
|
|
|
|
def HandleTypeDefDecl(Arch, Cursor, Name):
|
|
for Child in Cursor.get_children():
|
|
if (Child.kind == CursorKind.UNION_DECL):
|
|
pass
|
|
elif (Child.kind == CursorKind.STRUCT_DECL):
|
|
Arch = HandleStructDeclCursor(Arch, Child, Name)
|
|
elif (Child.kind == CursorKind.UNION_DECL):
|
|
Arch = HandleUnionDeclCursor(Arch, Child, Name)
|
|
elif (Child.kind == CursorKind.TYPEDEF_DECL):
|
|
Arch = HandleTypeDefDeclCursor(Arch, Child)
|
|
elif (Child.kind == CursorKind.TYPE_REF or
|
|
Child.kind == CursorKind.NAMESPACE_REF or
|
|
Child.kind == CursorKind.TEMPLATE_REF or
|
|
Child.kind == CursorKind.ALIGNED_ATTR):
|
|
# Safe to pass on
|
|
pass
|
|
else:
|
|
logging.critical ("Unhandled TypedefDecl {0}-{1}-{2}".format(Child.kind, Child.type.spelling, Child.spelling))
|
|
|
|
def HandleCursor(Arch, Cursor):
|
|
if (Cursor.kind.is_invalid()):
|
|
Diags = TU.diagnostics
|
|
for Diag in Diags:
|
|
logging.warning (Diag.format())
|
|
|
|
Arch.Parsed = False
|
|
return
|
|
|
|
for Child in Cursor.get_children():
|
|
if (Child.kind == CursorKind.TRANSLATION_UNIT):
|
|
Arch = HandleCursor(Arch, Child)
|
|
elif (Child.kind == CursorKind.FIELD_DECL):
|
|
pass
|
|
elif (Child.kind == CursorKind.UNION_DECL):
|
|
Arch = HandleUnionDeclCursor(Arch, Child)
|
|
elif (Child.kind == CursorKind.STRUCT_DECL):
|
|
Arch = HandleStructDeclCursor(Arch, Child)
|
|
elif (Child.kind == CursorKind.TYPEDEF_DECL):
|
|
Arch = HandleTypeDefDeclCursor(Arch, Child)
|
|
elif (Child.kind == CursorKind.VAR_DECL):
|
|
Arch = HandleVarDeclCursor(Arch, Child)
|
|
elif (Child.kind == CursorKind.NAMESPACE):
|
|
# Append namespace
|
|
Arch.NamespaceScope.append(Child.spelling)
|
|
SetNamespace(Arch)
|
|
|
|
# Handle children
|
|
Arch = HandleCursor(Arch, Child)
|
|
|
|
# Pop namespace off
|
|
Arch.NamespaceScope.pop()
|
|
SetNamespace(Arch)
|
|
elif (Child.kind == CursorKind.TYPE_REF):
|
|
# Safe to pass on
|
|
pass
|
|
elif (Child.kind == CursorKind.FUNCTION_DECL):
|
|
# For function printing
|
|
Arch = HandleFunctionDeclCursor(Arch, Child)
|
|
else:
|
|
Arch = HandleCursor(Arch, Child)
|
|
|
|
return Arch
|
|
|
|
def GetDB(Arch, filename, args):
|
|
Index = clang.cindex.Index.create()
|
|
try:
|
|
TU = Index.parse(filename, args=args, options=TranslationUnit.PARSE_INCOMPLETE)
|
|
except TranslationUnitLoadError:
|
|
Arch.Parsed = False
|
|
Diags = TU.diagnostics
|
|
for Diag in Diags:
|
|
logging.warning (Diag.format())
|
|
|
|
return
|
|
|
|
Arch.TU = TU
|
|
FunctionDecls.clear()
|
|
HandleCursor(Arch, TU.cursor)
|
|
|
|
# Get diagnostics
|
|
Diags = TU.diagnostics
|
|
if (len(Diags) != 0):
|
|
logging.warning ("Diagnostics from Arch: {0}".format(Arch.ArchName))
|
|
|
|
for Diag in Diags:
|
|
logging.warning (Diag.format())
|
|
|
|
return Arch
|
|
|
|
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 <Header.hpp> <clang arguments...>" % (sys.argv[0]))
|
|
|
|
Header = ""
|
|
BaseArgs = []
|
|
|
|
# Parse our arguments
|
|
Header = sys.argv[1]
|
|
|
|
# Add arguments for clang
|
|
for ArgIndex in range(2, len(sys.argv)):
|
|
BaseArgs.append(sys.argv[ArgIndex])
|
|
|
|
args_x86_64 = [
|
|
"-I/usr/include/x86_64-linux-gnu",
|
|
"-I/usr/x86_64-linux-gnu/include/c++/10/x86_64-linux-gnu/",
|
|
"-I/usr/x86_64-linux-gnu/include/",
|
|
"-O2",
|
|
"--target=x86_64-linux-unknown",
|
|
"-D_M_X86_64",
|
|
]
|
|
|
|
# Add all the arguments to the different lists
|
|
args_x86_64.extend(BaseArgs)
|
|
|
|
# We need to find the default arguments through clang invocations
|
|
args_x86_64 = FindClangArguments(args_x86_64)
|
|
|
|
Arch_x86_64 = ArchDB("x86_64")
|
|
Arch_x86_64 = GetDB(Arch_x86_64, Header, args_x86_64)
|
|
PrintFunctionDecls()
|
|
|
|
if __name__ == "__main__":
|
|
# execute only if run as a script
|
|
sys.exit(main())
|