diff --git a/README.md b/README.md index 6f7f20f..4da238e 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,46 @@ # What is DOL C-Kit? -DOL C-Kit is a toolkit for compiling C code (or assembly) using DevkitPPC to inject into a GameCube/Wii \*.dol executable. It has been written in such a way that it can be adapted to many different games. You will need [Python 3](https://www.python.org/downloads/) and [DevKitPPC](https://devkitpro.org/wiki/Getting_Started) installed to use it. As well, DOL C-Kit is dependent on [pyelftools](https://github.com/eliben/pyelftools), JoshuaMK's fork of [dolreader](https://github.com/JoshuaMKW/dolreader), and [geckocode-libs](https://github.com/JoshuaMKW/geckocode-libs). +DOL C-Kit is a toolkit for compiling C/C++ code (or assembly) using DevkitPPC to inject into a GameCube/Wii \*.dol executable. It has been written in such a way that it can be adapted to many different games. You will need [Python 3](https://www.python.org/downloads/) and [DevKitPPC](https://devkitpro.org/wiki/Getting_Started) installed to use it. As well, DOL C-Kit is dependent on [pyelftools](https://github.com/eliben/pyelftools), JoshuaMK's fork of [dolreader](https://github.com/JoshuaMKW/dolreader), and [geckocode-libs](https://github.com/JoshuaMKW/geckocode-libs). Credit to Yoshi2 for creating the original GC C-Kit. DOL C-Kit couldn't exist without it. -# How to use it / The Project Class +## How to use it DOL C-Kit is a Python module. To install on Windows, run INSTALL.bat as administrator. To install on Linux, run INSTALL.sh as superuser. -The Project class automates the tedious parts of compiling, linking, and injecting custom code into a \*.dol executable. The Project class constructor has two optional parameters to set the "base_addr" and "verbose" member variables. By default, they are set to None and False, respectively. +## The Project Class +`from dol_c_kit import Project` -The Project class has many member variables that may be directly modified: -* src_dir: Path to C code (or assembly) source files. Default is "". -* obj_dir: Path to output \*.o files and other files generated by DOL C-Kit to. Default is "". -* devkitppc_path: Change this if DevKitPPC is not installed at its default location. -* project_name: Name used for certain files generated by DOL C-Kit. Default is "project". -* gcc_flags: Non-crucial flags passed to powerpc-eabi-gcc. Defaults include "-w", "-std=c99", "-O1", and "-fno-asynchronous-unwind-tables". -* as_flags: Non-crucial flags passed to powerpc-eabi-as. Defaults include "-w". -* ld_flags: Non-crucial flags passed to powerpc-eabi-ld. Defaults include nothing. -* base_addr: The location new data will be put at. This is set by the constructor, but may be modified directly as well. -* sda_base: The value used for the \_SDA\_BASE\_ symbol. This is set by the set\_sda\_bases method, but may be modified directly as well. -* sda2_base: The value used for the \_SDA2\_BASE\_ symbol. This is set by the set\_sda\_bases method, but may be modified directly as well. -* verbose: Flag for additional information printing. This is set by the constructor, but may be modified directly as well. +The Project class automates the tedious parts of compiling, linking, and injecting custom code into a \*.dol executable. By shifting forward the stack, db_stack, and OSArenaLo, space for new data can be allocated. To do this, a patching function modifying a given game's "\_\_init_registers", "OSInit", and "\_\_OSThreadInit" functions must be written. The project's save_dol function passes two parameters to this patching function: a DolFile class, and the base_addr of your project. -## Step 1: Populate the project -* `add_c_file(filepath, gcc_flags=(), use_global_flags=True)`
-Add a C source file to the project. Two optional arguments may be given: gcc_flags is a tuple of strings passed to powerpc-eabi-gcc as flags, and use_global_flags determines if the gcc_flags member of the Project class are used for this source file. +### Class constructor +* `Project(self, base_addr=None, verbose=False)`
+The Project class constructor has two optional parameters. base_addr and verbose set their respective class members upon class construction. -* `add_asm_file(filepath, as_flags=(), use_global_flags=True)`
-Add an assembly source file to the project. Two optional arguments may be given: as_flags is a tuple of strings passed to powerpc-eabi-as as flags, and use_global_flags determines if the as_flags member of the Project class are used for this source file. +### Class members +The Project class has many member variables that may be directly modified: +* `src_dir` Path to C code (or assembly) source files. Default is "". +* `obj_dir` Path to output \*.o files and other files generated by DOL C-Kit to. Default is "". +* `devkitppc_path` Change this if DevKitPPC is not installed at its default location. +* `project_name` Name used for certain files generated by DOL C-Kit. Default is "project". +* `c_flags` Non-crucial flags passed to powerpc-eabi-gcc. Defaults include "-w", "-std=c99", "-O1", and "-fno-asynchronous-unwind-tables". +* `cpp_flags` Non-crucial flags passed to powerpc-eabi-g++. Defaults include "-w", "-std=c++98", "-O1", "-fno-asynchronous-unwind-tables", and "-fno-rtti". +* `asm_flags` Non-crucial flags passed to powerpc-eabi-as. Defaults include "-w". +* `linker_flags` Non-crucial flags passed to powerpc-eabi-ld. Defaults include nothing. +* `base_addr` The location new data will be put at. This is set by the constructor, but may be modified directly as well. +* `sda_base` The value used for the \_SDA\_BASE\_ symbol. This is set by the set\_sda\_bases method, but may be modified directly as well. +* `sda2_base` The value used for the \_SDA2\_BASE\_ symbol. This is set by the set\_sda\_bases method, but may be modified directly as well. +* `verbose` Flag for additional information printing. This is set by the constructor, but may be modified directly as well. + +### Step 1: Methods to populate the project +* `add_c_file(filepath, flags=(), use_global_flags=True)`
+Add a C source file to the project. Two optional arguments may be given: flags is a tuple of strings passed to powerpc-eabi-gcc as flags, and use_global_flags determines if the c_flags member of the Project class are used for this source file. + +* `add_cpp_file(filepath, flags=(), use_global_flags=True)`
+Add a C++ source file to the project. Two optional arguments may be given: flags is a tuple of strings passed to powerpc-eabi-g++ as flags, and use_global_flags determines if the cpp_flags member of the Project class are used for this source file. + +* `add_asm_file(filepath, flags=(), use_global_flags=True)`
+Add an assembly source file to the project. Two optional arguments may be given: flags is a tuple of strings passed to powerpc-eabi-as as flags, and use_global_flags determines if the asm_flags member of the Project class are used for this source file. * `add_obj_file(filepath, do_cleanup=False)`
Add an unlinked object file to the project. This object file must be in the obj_dir, not the src_dir. The cleanup method WILL DELETE FILES added by the add_obj_file method if the optional do_cleanup argument is True. @@ -69,7 +81,7 @@ Give your project a game-specific patching function to use to allocate space for * `set_sda_bases(sda_base, sda2_base)`
Set the \_SDA\_BASE\_ and \_SDA2\_BASE\_ symbols. These values get passed to the linker. They are also important for the @sda and @sda2 modifiers for Immediate16Hooks. -## Step 2: Build the project +### Step 2: Methods to build the project * `build_dol(in_dol_path, out_dol_path)`
Compile, assemble, and link all source files, hooks, and supported Gecko Codes into a \*.dol executable. If no base_addr is specified, the ROM end will automatically be detected and used. A new text section will be allocated to contain the new data. If no text sections are available, a data section will be allocated instead.
Note: Automatic ROM end detection does not work for DOLs that allocate space for .sbss2. @@ -82,3 +94,62 @@ Generate a CodeWarrior-like symbol map from the project. Run this after buildin * `cleanup()`
Delete unimportant files created by DOL C-Kit. This includes unlinked \*.o files, and .o, .bin, and .map. + +# How to work with mangled symbols (C++) +In C++, there is the concept of mangled symbol names. For example, the function signature `int foo::bar(MyClass arg1)` becomes the symbol `_ZN3foo3barE7MyClass`. DOL C-Kit provides faculties to make working with mangled symbols easy. + +## The LDPlusPlus class +`from dol_c_kit import LDPlusPlus` + +Just like a usual linker script to give dol-side symbols a value for C and ASM, one is needed for C++ as well. LDPlusPlus makes this easier by mangling function signatures in bulk for you. + +### Class constructor +* `LDPlusPlus(abi)`
+The LDPlusPlus class constructor has one parameter. abi is the desired ABI (an enum value). At the moment, only ABI.Itanium is available. + +### Methods +* `assign(prototype, value)`
+[Assign](https://sourceware.org/binutils/docs/ld/Simple-Assignments.html) a value to a symbol in the linker script. + +* `provide(prototype, value)`
+[Provide](https://sourceware.org/binutils/docs/ld/PROVIDE.html) a value for a symbol in the linker script. + +* `save(filepath)`
+Save the linker script to a given filepath. + +## mangle (and itanium_mangle) +`from dol_c_kit import mangle, itanium_mangle` + +To mangle an individual signature, use the mangle function (or itanium_mangle function). This is useful for hooks which take a symbol name as an argument. + +* `mangle(prototype, abi)`
+Returns a mangled symbol from a given signature prototype in a given ABI (an enum value). At the moment, only ABI.Itanium is available. + +* `itanium_mangle(prototype)`
+Returns a mangled symbol from a given signature prototype in the Itanium ABI. + +## Limitations and workarounds of the mangler +Writing a C++ mangler that has no concept of user defined types, or really any context at all, was a struggle that came with a few compromises. +* Function pointer types are not supported. +* Certain kinds of template instancing are not supported. +* Typedefs are not supported (yet). +* To mangle certain edge cases, special tokens starting with "$$" are used: + * `$$vtable` is used for vtable signatures. e.g. `ClassA::$$vtable` will mangle to `_ZTV6ClassA`. + * `$$rtti` is used for typeinfo signatures. e.g. `ClassA::$$rtti` will mangle to `_ZTI6ClassA`. + * `$$vtt_structure` is used for vtable structure signatures. e.g. `ClassA::$$vtt_structure` will mangle to `_ZTT6ClassA`. + * `$$rtti_name` is used for typeinfo names. e.g. `ClassA::$$rtti_name` will mangle to `_ZTS6ClassA`. + * `$$ctor` is used for (the assumed default) object constructors. e.g. `ClassA::$$ctor` will mangle to `_ZN6ClassAC1Ev`. + * `$$ctor1` is used for complete object constructors. e.g. `ClassA::$$ctor1` will mangle to `_ZN6ClassAC1Ev`. + * `$$ctor2` is used for base object constructors. e.g. `ClassA::$$ctor2` will mangle to `_ZN6ClassAC2Ev`. + * `$$ctor3` is used for complete object allocating. e.g. `ClassA::$$ctor3` will mangle to `_ZN6ClassAC3Ev`. + * `$$dtor` is used for (the assumed default) object destructors. e.g. `ClassA::$$dtor` will mangle to `_ZN6ClassAD1Ev`. + * `$$dtor0` is used for deleting destructors. e.g. `ClassA::$$dtor0` will mangle to `_ZN6ClassAD0Ev`. + * `$$dtor1` is used for complete object destructors. e.g. `ClassA::$$dtor1` will mangle to `_ZN6ClassAD1Ev`. + * `$$dtor2` is used for base object destructors. e.g. `ClassA::$$dtor2` will mangle to `_ZN6ClassAD2Ev`. + * `$$unary` is used for operator overloads that are ambigious without context. + * `operator+ $$unary` will produce the "positive" operator override rather than the "add" operator override. + * `operator- $$unary` will produce the "negative" operator override rather than the "subtract" operator override. + * `operator& $$unary` will produce the "reference" operator override rather than the "bitwise AND" operator override. + * `operator* $$unary` will produce the "dereference" operator override rather than the "multiply" operator override. + +There are likely other cases that the mangler won't cover, but for 99% of signatures, it should work. \ No newline at end of file diff --git a/dol_c_kit/__init__.py b/dol_c_kit/__init__.py index abb08df..e9618be 100644 --- a/dol_c_kit/__init__.py +++ b/dol_c_kit/__init__.py @@ -1,4 +1,4 @@ -__version__ = "2.4.1" +__version__ = "3.0.0" __author__ = "Minty Meeo" __credits__ = "Yoshi2 (RenolY2)" @@ -24,5 +24,9 @@ from dol_c_kit.doltools import write_li from dol_c_kit.doltools import write_lis from dol_c_kit.doltools import write_nop +from dol_c_kit.mangle import ABI +from dol_c_kit.mangle import LDPlusPlus +from dol_c_kit.mangle import mangle +from dol_c_kit.mangle import itanium_mangle from dol_c_kit.devkit_tools import Project diff --git a/dol_c_kit/devkit_tools.py b/dol_c_kit/devkit_tools.py index fc6fd95..3ce3803 100644 --- a/dol_c_kit/devkit_tools.py +++ b/dol_c_kit/devkit_tools.py @@ -287,12 +287,14 @@ def __init__(self, base_addr=None, verbose=False): self.obj_dir = "" self.project_name = "project" self.c_files = [] + self.cpp_files = [] self.asm_files = [] self.obj_files = [] self.linker_script_files = [] - self.gcc_flags = ["-w", "-std=c99", "-O1", "-fno-asynchronous-unwind-tables",] - self.as_flags = ["-w",] - self.ld_flags = [] + self.c_flags = ["-w", "-std=c99", "-O1", "-fno-asynchronous-unwind-tables",] + self.cpp_flags = ["-w", "-std=c++98", "-O1", "-fno-asynchronous-unwind-tables", "-fno-rtti",] + self.asm_flags = ["-w",] + self.linker_flags = [] self.symbols = {} self.verbose = verbose @@ -307,11 +309,14 @@ def __init__(self, base_addr=None, verbose=False): # Add stuff - def add_c_file(self, filepath, gcc_flags=(), use_global_flags=True): - self.c_files.append((filepath, gcc_flags, use_global_flags)) + def add_c_file(self, filepath, flags=(), use_global_flags=True): + self.c_files.append((filepath, flags, use_global_flags)) - def add_asm_file(self, filepath, as_flags=(), use_global_flags=True): - self.asm_files.append((filepath, as_flags, use_global_flags)) + def add_cpp_file(self, filepath, flags=(), use_global_flags=True): + self.cpp_files.append((filepath, flags, use_global_flags)) + + def add_asm_file(self, filepath, flags=(), use_global_flags=True): + self.asm_files.append((filepath, flags, use_global_flags)) def add_obj_file(self, filepath, do_cleanup=False): self.obj_files.append((filepath, do_cleanup)) @@ -549,12 +554,12 @@ def cleanup(self): # Private stuff - def __compile(self, infile, gcc_flags, use_global_flags): + def __compile(self, infile, flags, use_global_flags): args = [self.devkitppc_path+"powerpc-eabi-gcc", "-c", self.src_dir+infile, "-o", self.obj_dir+infile+".o", "-I", self.src_dir] if use_global_flags: - for flag in self.gcc_flags: + for flag in self.c_flags: args.append(flag) - for flag in gcc_flags: + for flag in flags: args.append(flag) if self.verbose: print(args) @@ -562,12 +567,25 @@ def __compile(self, infile, gcc_flags, use_global_flags): self.obj_files.append((infile+".o", True)) return True - def __assemble(self, infile, as_flags, use_global_flags): + def __compileplusplus(self, infile, flags, use_global_flags): + args = [self.devkitppc_path+"powerpc-eabi-g++", "-c", self.src_dir+infile, "-o", self.obj_dir+infile+".o", "-I", self.src_dir] + if use_global_flags: + for flag in self.cpp_flags: + args.append(flag) + for flag in flags: + args.append(flag) + if self.verbose: + print(args) + subprocess.call(args) + self.obj_files.append((infile+".o", True)) + return True + + def __assemble(self, infile, flags, use_global_flags): args = [self.devkitppc_path+"powerpc-eabi-as", self.src_dir+infile, "-o", self.obj_dir+infile+".o", "-I", self.src_dir] if use_global_flags: - for flag in self.as_flags: + for flag in self.asm_flags: args.append(flag) - for flag in as_flags: + for flag in flags: args.append(flag) if self.verbose: print(args) @@ -594,7 +612,7 @@ def __link_project(self): for filename, do_cleanup in self.obj_files: args.append(self.obj_dir+filename) args.extend(("-Map", self.obj_dir+self.project_name+".map")) - for flag in self.ld_flags: + for flag in self.linker_flags: args.append(flag) if self.verbose: print(args) @@ -631,11 +649,14 @@ def __build_project(self): is_linked = False is_processed = False - for filepath, gcc_flags, use_global_flags in self.c_files: - is_built |= self.__compile(filepath, gcc_flags, use_global_flags) + for filepath, flags, use_global_flags in self.c_files: + is_built |= self.__compile(filepath, flags, use_global_flags) + + for filepath, flags, use_global_flags in self.cpp_files: + is_built |= self.__compileplusplus(filepath, flags, use_global_flags) - for filepath, as_flags, use_global_flags in self.asm_files: - is_built |= self.__assemble(filepath, as_flags, use_global_flags) + for filepath, flags, use_global_flags in self.asm_files: + is_built |= self.__assemble(filepath, flags, use_global_flags) if is_built == True: is_linked |= self.__link_project() diff --git a/dol_c_kit/mangle.py b/dol_c_kit/mangle.py new file mode 100644 index 0000000..0522c90 --- /dev/null +++ b/dol_c_kit/mangle.py @@ -0,0 +1,1331 @@ +from enum import Enum + +# Massive thank you to these two documents: +# https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling +# https://github.com/gchatelet/gcc_cpp_mangling_documentation + +# Mutable String class + +class MutableString(list): + def __init__(self, string): + for char in string: + self.append(char) + + def __str__(self): + return "".join(char for char in self) + + def pop_front(self, num): + ret = "" + for i in range(num): + ret += self.pop(0) + return ret + + def pop_back(self, num): + ret = "" + for i in range(num): + ret += self.pop() + return ret + + def startswith(self, string): + return str(self).startswith(string) + +# Simple Token class + +class SyntaxToken(str): + def __init__(self, string): + self = string + + def itanium_mangle(self, compressibles): + return "{}{}".format(len(self), self) + +# Special Tokens + +class SpecialTokenCtor(object): + def __init__(self, num = 1): + self.num = num + def __str__(self): + return "$$ctor{}".format(self.num) + def __repr__(self): + return "\'$$ctor{}\'".format(self.num) + def itanium_mangle(self, compressibles): + return "C{}".format(self.num) + +class SpecialTokenDtor(object): + def __init__(self, num = 1): + self.num = num + def __str__(self): + return "$$dtor{}".format(self.num) + def __repr__(self): + return "\'$$dtor{}\'".format(self.num) + def itanium_mangle(self, compressibles): + return "D{}".format(self.num) + +class SpecialTokenVTable(object): + def __str__(self): + return "$$vtable" + def __repr__(self): + return "\'$$vtable\'" + def itanium_mangle(self, compressibles): + return "TV" + +class SpecialTokenRTTI(object): + def __str__(self): + return "$$rtti" + def __repr__(self): + return "\'$$rtti\'" + def itanium_mangle(self, compressibles): + return "TI" + +class SpecialTokenVTTStructure(object): # Itanium exclusive? + def __str__(self): + return "$$vtt_structure" + def __repr__(self): + return "\'$$vtt_structure\'" + def itanium_mangle(self, compressibles): + return "TT" + +class SpecialTokenRTTIName(object): # Itanium exclusive? + def __str__(self): + return "$$rtti_name" + def __repr__(self): + return "\'$$rtti_name\'" + def itanium_mangle(self, compressibles): + return "TS" + +class SpecialTokenUnary(object): # This one is extra special. It should never be mangled, only provide context and be removed. + def __str__(self): + return "$$unary" + def __repr__(self): + return "\'$$unary\'" + +# Special Tokens (operator overrides) + +class SpecialOperatorNew(object): + def __str__(self): + return "operator new" + def __repr__(self): + return "\'operator new\'" + def itanium_mangle(self, compressibles): + return "nw" + +class SpecialOperatorNewArray(object): + def __str__(self): + return "operator new[]" + def __repr__(self): + return "\'operator new[]\'" + def itanium_mangle(self, compressibles): + return "na" + +class SpecialOperatorDelete(object): + def __str__(self): + return "operator delete" + def __repr__(self): + return "\'operator delete\'" + def itanium_mangle(self, compressibles): + return "dl" + +class SpecialOperatorDeleteArray(object): + def __str__(self): + return "operator delete[]" + def __repr__(self): + return "\'operator delete[]\'" + def itanium_mangle(self, compressibles): + return "da" + +class SpecialOperatorCoAwait(object): + def __str__(self): + return "operator co_await" + def __repr__(self): + return "\'operator co_await\'" + def itanium_mangle(self, compressibles): + return "aw" + +class SpecialOperatorPositive(object): # unary + def __str__(self): + return "operator + (unary)" + def __repr__(self): + return "\'operator + (unary)\'" + def itanium_mangle(self, compressibles): + return "ps" + +class SpecialOperatorNegative(object): # unary + def __str__(self): + return "operator - (unary)" + def __repr__(self): + return "\'operator - (unary)\'" + def itanium_mangle(self, compressibles): + return "ng" + +class SpecialOperatorReference(object): # unary + def __str__(self): + return "operator & (unary)" + def __repr__(self): + return "\'operator & (unary)\'" + def itanium_mangle(self, compressibles): + return "ad" # address of + +class SpecialOperatorDereference(object): # unary + def __str__(self): + return "operator * (unary)" + def __repr__(self): + return "\'operator * (unary)\'" + def itanium_mangle(self, compressibles): + return "de" + +class SpecialOperatorBitwiseNOT(object): + def __str__(self): + return "operator ~" + def __repr__(self): + return "\'operator ~\'" + def itanium_mangle(self, compressibles): + return "co" + +class SpecialOperatorAdd(object): + def __str__(self): + return "operator +" + def __repr__(self): + return "\'operator +\'" + def itanium_mangle(self, compressibles): + return "pl" + +class SpecialOperatorSubtract(object): + def __str__(self): + return "operator -" + def __repr__(self): + return "\'operator -\'" + def itanium_mangle(self, compressibles): + return "mi" + +class SpecialOperatorMultiply(object): + def __str__(self): + return "operator *" + def __repr__(self): + return "\'operator *\'" + def itanium_mangle(self, compressibles): + return "ml" + +class SpecialOperatorDivide(object): + def __str__(self): + return "operator /" + def __repr__(self): + return "\'operator /\'" + def itanium_mangle(self, compressibles): + return "dv" + +class SpecialOperatorModulo(object): + def __str__(self): + return "operator %" + def __repr__(self): + return "\'operator %\'" + def itanium_mangle(self, compressibles): + return "rm" # remainder + +class SpecialOperatorBitwiseAND(object): + def __str__(self): + return "operator &" + def __repr__(self): + return "\'operator &\'" + def itanium_mangle(self, compressibles): + return "an" + +class SpecialOperatorBitwiseOR(object): + def __str__(self): + return "operator |" + def __repr__(self): + return "\'operator |\'" + def itanium_mangle(self, compressibles): + return "or" + +class SpecialOperatorBitwiseXOR(object): + def __str__(self): + return "operator ^" + def __repr__(self): + return "\'operator ^\'" + def itanium_mangle(self, compressibles): + return "eo" # exclusive or + +class SpecialOperatorAssign(object): + def __str__(self): + return "operator =" + def __repr__(self): + return "\'operator =\'" + def itanium_mangle(self, compressibles): + return "aS" + +class SpecialOperatorAssignAdd(object): + def __str__(self): + return "operator +=" + def __repr__(self): + return "\'operator +=\'" + def itanium_mangle(self, compressibles): + return "pL" + +class SpecialOperatorAssignSubtract(object): + def __str__(self): + return "operator -=" + def __repr__(self): + return "\'operator -=\'" + def itanium_mangle(self, compressibles): + return "mI" + +class SpecialOperatorAssignMultiply(object): + def __str__(self): + return "operator *=" + def __repr__(self): + return "\'operator *=\'" + def itanium_mangle(self, compressibles): + return "mL" + +class SpecialOperatorAssignDivide(object): + def __str__(self): + return "operator /=" + def __repr__(self): + return "\'operator /=\'" + def itanium_mangle(self, compressibles): + return "dV" + +class SpecialOperatorAssignModulo(object): + def __str__(self): + return "operator &=" + def __repr__(self): + return "\'operator &=\'" + def itanium_mangle(self, compressibles): + return "rM" # remainder + +class SpecialOperatorAssignBitwiseAND(object): + def __str__(self): + return "operator &=" + def __repr__(self): + return "\'operator &=\'" + def itanium_mangle(self, compressibles): + return "aN" + +class SpecialOperatorAssignBitwiseOR(object): + def __str__(self): + return "operator |=" + def __repr__(self): + return "\'operator |=\'" + def itanium_mangle(self, compressibles): + return "oR" + +class SpecialOperatorAssignBitwiseXOR(object): + def __str__(self): + return "operator ^=" + def __repr__(self): + return "\'operator ^=\'" + def itanium_mangle(self, compressibles): + return "eO" # exclusive or + +class SpecialOperatorLeftShift(object): + def __str__(self): + return "operator <<" + def __repr__(self): + return "\'operator <<\'" + def itanium_mangle(self, compressibles): + return "ls" + +class SpecialOperatorRightShift(object): + def __str__(self): + return "operator >>" + def __repr__(self): + return "\'operator >>\'" + def itanium_mangle(self, compressibles): + return "rs" + +class SpecialOperatorAssignLeftShift(object): + def __str__(self): + return "operator <<=" + def __repr__(self): + return "\'operator <<=\'" + def itanium_mangle(self, compressibles): + return "lS" + +class SpecialOperatorAssignRightShift(object): + def __str__(self): + return "operator >>=" + def __repr__(self): + return "\'operator >>=\'" + def itanium_mangle(self, compressibles): + return "rS" + +class SpecialOperatorEqual(object): + def __str__(self): + return "operator ==" + def __repr__(self): + return "\'operator ==\'" + def itanium_mangle(self, compressibles): + return "eq" + +class SpecialOperatorNotEqual(object): + def __str__(self): + return "operator !=" + def __repr__(self): + return "\'operator !=\'" + def itanium_mangle(self, compressibles): + return "ne" + +class SpecialOperatorLesserThan(object): + def __str__(self): + return "operator <" + def __repr__(self): + return "\'operator <\'" + def itanium_mangle(self, compressibles): + return "lt" + +class SpecialOperatorGreaterThan(object): + def __str__(self): + return "operator >" + def __repr__(self): + return "\'operator >\'" + def itanium_mangle(self, compressibles): + return "gt" + +class SpecialOperatorLesserThanOrEqual(object): + def __str__(self): + return "operator <=" + def __repr__(self): + return "\'operator <=\'" + def itanium_mangle(self, compressibles): + return "le" + +class SpecialOperatorGreaterThanOrEqual(object): + def __str__(self): + return "operator >=" + def __repr__(self): + return "\'operator >=\'" + def itanium_mangle(self, compressibles): + return "ge" + +class SpecialOperatorSpaceShip(object): + def __str__(self): + return "operator <=>" + def __repr__(self): + return "\'operator <=>\'" + def itanium_mangle(self, compressibles): + return "ss" + +class SpecialOperatorLogicalNOT(object): + def __str__(self): + return "operator !" + def __repr__(self): + return "\'operator !\'" + def itanium_mangle(self, compressibles): + return "nt" + +class SpecialOperatorLogicalAND(object): + def __str__(self): + return "operator &&" + def __repr__(self): + return "\'operator &&\'" + def itanium_mangle(self, compressibles): + return "aa" + +class SpecialOperatorLogicalOR(object): + def __str__(self): + return "operator ||" + def __repr__(self): + return "\'operator ||\'" + def itanium_mangle(self, compressibles): + return "oo" + +class SpecialOperatorIncrement(object): + def __str__(self): + return "operator ++" + def __repr__(self): + return "\'operator ++\'" + def itanium_mangle(self, compressibles): + return "pp" + +class SpecialOperatorDecrement(object): + def __str__(self): + return "operator --" + def __repr__(self): + return "\'operator --\'" + def itanium_mangle(self, compressibles): + return "mm" + +class SpecialOperatorArraySubscript(object): + def __str__(self): + return "operator []" + def __repr__(self): + return "\'operator []\'" + def itanium_mangle(self, compressibles): + return "ix" + +# There are a few more operator overrides but I genuinely don't understand them. I'm sorry. + +# Built-in types + +class BuiltinVoid(object): + def __str__(self): + return "void" + def __repr__(self): + return "\'void\'" + def itanium_mangle(self, compressibles): + return "v" + +class BuiltinBool(object): + def __str__(self): + return "bool" + def __repr__(self): + return "\'bool\'" + def itanium_mangle(self, compressibles): + return "b" + +class BuiltinChar(object): + def __str__(self): + return "char" + def __repr__(self): + return "\'char\'" + def itanium_mangle(self, compressibles): + return "c" + +class BuiltinShort(object): + def __str__(self): + return "short" + def __repr__(self): + return "\'short\'" + def itanium_mangle(self, compressibles): + return "s" + +class BuiltinInt(object): + def __str__(self): + return "int" + def __repr__(self): + return "\'int\'" + def itanium_mangle(self, compressibles): + return "i" + +class Builtin__int64(object): + def __str__(self): + return "__int64" + def __repr__(self): + return "\'__int64\'" + def itanium_mangle(self, compressibles): + return "x" + +class Builtin__int128(object): + def __str__(self): + return "__int128" + def __repr__(self): + return "\'__int128\'" + def itanium_mangle(self, compressibles): + return "n" + +class BuiltinFloat(object): + def __str__(self): + return "float" + def __repr__(self): + return "\'float\'" + def itanium_mangle(self, compressibles): + return "f" + +class BuiltinDouble(object): + def __str__(self): + return "double" + def __repr__(self): + return "\'double\'" + def itanium_mangle(self, compressibles): + return "d" + +class Builtin__float80(object): + def __str__(self): + return "__float80" + def __repr__(self): + return "\'__float80\'" + def itanium_mangle(self, compressibles): + return "e" + +class Builtin__float128(object): + def __str__(self): + return "__float128" + def __repr__(self): + return "\'__float128\'" + def itanium_mangle(self, compressibles): + return "g" + +class BuiltinEllipses(object): + def __str__(self): + return "..." + def __repr__(self): + return "\'...\'" + def itanium_mangle(self, compressibles): + return "z" + +# Decorator Classes + +class OperatorUnsigned(object): + def __init__(self): + self.rhand = None + + def __str__(self): + return "unsigned {}".format(self.rhand) + def __repr__(self): + return "\'unsigned\' {}".format(repr(self.rhand)) + + def itanium_mangle(self, compressibles): + rhand_symbol = self.rhand.itanium_mangle(compressibles) + if rhand_symbol == "c": # char + return "h" + if rhand_symbol == "s": # short + return "t" + if rhand_symbol == "i": # int + return "j" + if rhand_symbol == "l": # long int + return "m" + if rhand_symbol == "x": # long long int + return "y" + if rhand_symbol == "n": # __int128 + return "o" + raise Exception("Type {} is incompatible with unsigned decorator!".format(rhand_symbol)) + +class OperatorSigned(object): + def __init__(self): + self.rhand = None + + def __str__(self): + return "signed {}".format(self.rhand) + def __repr__(self): + return "\'signed\' {}".format(repr(self.rhand)) + + def itanium_mangle(self, compressibles): + rhand_symbol = self.rhand.itanium_mangle(compressibles) + if rhand_symbol == "c": # char + return "a" + if rhand_symbol == "s": # short + return "s" + if rhand_symbol == "i": # int + return "i" + if rhand_symbol == "l": # long int + return "l" + if rhand_symbol == "x": # long long int + return "x" + if rhand_symbol == "n": # __int128 + return "n" + raise Exception("Type {} is incompatible with signed decorator!".format(rhand_symbol)) + +class OperatorLong(object): + def __init__(self): + self.rhand = None + + def __str__(self): + return "long {}".format(self.rhand) + def __repr__(self): + return "\'long\' {}".format(repr(self.rhand)) + + def itanium_mangle(self, compressibles): + rhand_symbol = self.rhand.itanium_mangle(compressibles) + if rhand_symbol == "i": # int + return "l" + if rhand_symbol == "l": # long int + return "x" + if rhand_symbol == "d": # double + return "e" + raise Exception("Type {} is incompatible with long decorator!".format(rhand_symbol)) + +class OperatorConst(object): + def __init__(self): + self.lhand = None + + def __str__(self): + return "{} const".format(self.lhand) + def __repr__(self): + return "{} \'const\'".format(repr(self.lhand)) + + def itanium_mangle(self, compressibles): + key = str(self.lhand) + if key in compressibles: + lhand_symbol = compressibles[key] + else: + lhand_symbol = self.lhand.itanium_mangle(compressibles) + if type(self.lhand) not in builtin_types: + compressibles.register(key) + + # Constness does not matter until indirection happens + return lhand_symbol + +class OperatorPointer(object): + def __init__(self): + self.lhand = None + + def __str__(self): + return "{} *".format(self.lhand) + def __repr__(self): + return "{} \'*\'".format(repr(self.lhand)) + + def itanium_mangle(self, compressibles): + key = str(self.lhand) + if key in compressibles: + lhand_symbol = compressibles[key] + else: + lhand_symbol = self.lhand.itanium_mangle(compressibles) + if type(self.lhand) not in builtin_types: + compressibles.register(key) + + if type(self.lhand) == OperatorConst: + return "PK{}".format(lhand_symbol) + else: + return "P{}".format(lhand_symbol) + +class OperatorReference(object): + def __init__(self): + self.lhand = None + + def __str__(self): + return "{} &".format(self.lhand) + def __repr__(self): + return "{} \'&\'".format(repr(self.lhand)) + + def itanium_mangle(self, compressibles): + key = str(self.lhand) + if key in compressibles: + lhand_symbol = compressibles[key] + else: + lhand_symbol = self.lhand.itanium_mangle(compressibles) + if type(self.lhand) not in builtin_types: + compressibles.register(key) + + if type(self.lhand) == OperatorConst: + return "RK{}".format(lhand_symbol) + else: + return "R{}".format(lhand_symbol) + +class OperatorRHandReference(object): + def __init__(self): + self.lhand = None + + def __str__(self): + return "{} &&".format(self.lhand) + def __repr__(self): + return "{} \'&&\'".format(repr(self.lhand)) + + def itanium_mangle(self, compressibles): + key = str(self.lhand) + if key in compressibles: + lhand_symbol = compressibles[key] + else: + lhand_symbol = self.lhand.itanium_mangle(compressibles) + if type(self.lhand) not in builtin_types: + compressibles.register(key) + + if type(self.lhand) == OperatorConst: + return "OK{}".format(lhand_symbol) + else: + return "O{}".format(lhand_symbol) + +# Complex Classes + +class OperatorNamespace(object): + def __init__(self): + self.lhand = None + self.rhand = None + + def __str__(self): + return "{} :: {}".format(self.lhand, self.rhand) + def __repr__(self): + return "{} \'::\' {}".format(repr(self.lhand), repr(self.rhand)) + + def itanium_mangle(self, compressibles): + if self.lhand == "std": + return "St" + self.rhand.itanium_mangle(compressibles) + if type(self.rhand) == SpecialTokenVTable \ + or type(self.rhand) == SpecialTokenRTTI \ + or type(self.rhand) == SpecialTokenVTTStructure \ + or type(self.rhand) == SpecialTokenRTTIName: + return self.rhand.itanium_mangle(compressibles) + self.lhand.itanium_mangle(compressibles) # I hate this dirty hack + return "N" + self.itanium_mangle_ns(compressibles) + "E" + + def itanium_mangle_ns(self, compressibles): + key = str(self.lhand) + if key in compressibles: + return compressibles[key] + self.rhand.itanium_mangle(compressibles) + else: + if type(self.lhand) == OperatorNamespace: + lhand_symbol = self.lhand.itanium_mangle_ns(compressibles) + if type(self.lhand) not in builtin_types: + compressibles.register(key) + rhand_symbol = self.rhand.itanium_mangle(compressibles) + else: + lhand_symbol = self.lhand.itanium_mangle(compressibles) + if type(self.lhand) not in builtin_types: + compressibles.register(key) + rhand_symbol = self.rhand.itanium_mangle(compressibles) + return lhand_symbol + rhand_symbol + +class OperatorTemplateArgs(list): + def __init__(self, mutstring): + self.lhand = None + + mutstring.pop(0) + while mutstring: + char = mutstring[0] + if char in whitespace: + mutstring.pop(0) + continue + if char == ',': + mutstring.pop(0) + continue + if char == ')': + raise Exception("\')\' was unexpected at this time.") + if char == '>': + mutstring.pop(0) + if not self: + raise Exception("TODO: what is the behavior for empty template arguments?") + return + self.append(Expression(mutstring)[0]) + raise Exception("No closing brace found!") + + def __str__(self): + return "{} < {} >".format(self.lhand, ", ".join(str(iter) for iter in self)) + def __hash__(self): + return hash((tuple(self), self.lhand)) + + def itanium_mangle(self, compressibles): + # Lefthand mangles first + key = str(self.lhand) + if key in compressibles: + lhand_symbol = compressibles[key] + else: + lhand_symbol = self.lhand.itanium_mangle(compressibles) + if type(self.lhand) not in builtin_types: + compressibles.register(key) + + # Arguments mangle second + args = "" + for iter in self: + key = str(iter) + if key in compressibles: + args += compressibles[key] + else: + args += iter.itanium_mangle(compressibles) + if type(iter) not in builtin_types: + compressibles.register(key) + + # Finally, the entire thing mangles third + key = str(self) + if key in compressibles: + return compressibles[key] + compressibles.register(key) + + return "{}I{}E".format(lhand_symbol, args) + +class OperatorFunctionArgs(list): + def __init__(self, mutstring): + mutstring.pop(0) + while mutstring: + char = mutstring[0] + if char in whitespace: + mutstring.pop(0) + continue + if char == ',': + mutstring.pop(0) + continue + if char == ')': + mutstring.pop(0) + if not self: + self.append(BuiltinVoid()) + return + if char == '>': + raise Exception("\'>\' was unexpected at this time.") + self.append(Expression(mutstring)[0]) + raise Exception("No closing brace found!") + + def __str__(self): + return "( {} )".format(", ".join(str(iter) for iter in self)) + + def itanium_mangle(self, compressibles): + args = "" + for iter in self: + key = str(iter) + if key in compressibles: + args += compressibles[key] + else: + args += iter.itanium_mangle(compressibles) + if type(iter) not in builtin_types: + compressibles.register(key) + return args + +class Expression(list): + def __init__(self, mutstring = None): + while mutstring: + if mutstring[0] in whitespace: + mutstring.pop(0) + continue + + # Operators (and ellipses because idk how else to parse them) + if mutstring.startswith("*"): + mutstring.pop_front(1) + self.append(OperatorPointer()) + continue + if mutstring.startswith("&&"): + mutstring.pop_front(2) + self.append(OperatorRHandReference()) + continue + if mutstring.startswith("&"): + mutstring.pop_front(1) + self.append(OperatorReference()) + continue + if mutstring.startswith("::"): + mutstring.pop_front(2) + self.append(OperatorNamespace()) + continue + if mutstring.startswith("..."): + mutstring.pop_front(3) + self.append(BuiltinEllipses()) + continue + + # Argument Lists + char = mutstring[0] + if char == '(': + self.append(OperatorFunctionArgs(mutstring)) + continue + if char == '<': + self.append(OperatorTemplateArgs(mutstring)) + continue + if char == ")" \ + or char == ">" \ + or char == ",": + break + + # Get a string + curr_string = "" + while mutstring: + if mutstring[0] not in alphanumeric and mutstring[0] != '$': # '$' is for special tokens + break + curr_string += mutstring.pop(0) + + # const, unsigned, signed, long + # plus, built-in types and a redundant ellipses check + if \ + ( self.append(OperatorConst()) ,) if curr_string == "const" else \ + ( self.append(OperatorUnsigned()) ,) if curr_string == "unsigned" else \ + ( self.append(OperatorSigned()) ,) if curr_string == "signed" else \ + ( self.append(OperatorLong()) ,) if curr_string == "long" else \ + ( self.append(BuiltinVoid()) ,) if curr_string == "void" else \ + ( self.append(BuiltinBool()) ,) if curr_string == "bool" else \ + ( self.append(BuiltinChar()) ,) if curr_string == "char" else \ + ( self.append(BuiltinShort()) ,) if curr_string == "short" else \ + ( self.append(BuiltinInt()) ,) if curr_string == "int" else \ + ( self.append(Builtin__int64()) ,) if curr_string == "__int64" else \ + ( self.append(Builtin__int128()) ,) if curr_string == "__int128" else \ + ( self.append(BuiltinFloat()) ,) if curr_string == "float" else \ + ( self.append(BuiltinDouble()) ,) if curr_string == "double" else \ + ( self.append(Builtin__float80()) ,) if curr_string == "__float80" else \ + ( self.append(Builtin__float128()),) if curr_string == "__float128" else \ + ( self.append(BuiltinEllipses()) ,) if curr_string == "..." else (): + continue + + # SpecialTokens + if \ + ( self.append(SpecialTokenUnary()) ,) if curr_string == "$$unary" else \ + ( self.append(SpecialTokenCtor()) ,) if curr_string == "$$ctor" else \ + ( self.append(SpecialTokenCtor(1)) ,) if curr_string == "$$ctor1" else \ + ( self.append(SpecialTokenCtor(2)) ,) if curr_string == "$$ctor2" else \ + ( self.append(SpecialTokenCtor(3)) ,) if curr_string == "$$ctor3" else \ + ( self.append(SpecialTokenDtor()) ,) if curr_string == "$$dtor" else \ + ( self.append(SpecialTokenDtor(0)) ,) if curr_string == "$$dtor0" else \ + ( self.append(SpecialTokenDtor(1)) ,) if curr_string == "$$dtor1" else \ + ( self.append(SpecialTokenDtor(2)) ,) if curr_string == "$$dtor2" else \ + ( self.append(SpecialTokenVTable()) ,) if curr_string == "$$vtable" else \ + ( self.append(SpecialTokenRTTI()) ,) if curr_string == "$$rtti" else \ + ( self.append(SpecialTokenVTTStructure()),) if curr_string == "$$vtt_structure" else \ + ( self.append(SpecialTokenRTTIName()) ,) if curr_string == "$$rtti_name" else (): + continue + + # Operator overrides (are complicated) + if curr_string == "operator": + while mutstring[0] in whitespace: + mutstring.pop(0) + + # Non-alphanumeric operators. Also, this is a grotesque use of the ternary operator, tuples, and line continuations. + if \ + ( mutstring.pop_front(3), self.append(SpecialOperatorAssignLeftShift()) ) if mutstring.startswith("<<=") else \ + ( mutstring.pop_front(3), self.append(SpecialOperatorAssignRightShift()) ) if mutstring.startswith(">>=") else \ + ( mutstring.pop_front(3), self.append(SpecialOperatorSpaceShip()) ) if mutstring.startswith("<=>") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorAssignAdd()) ) if mutstring.startswith("+=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorAssignSubtract()) ) if mutstring.startswith("-=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorAssignMultiply()) ) if mutstring.startswith("*=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorAssignDivide()) ) if mutstring.startswith("/=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorAssignModulo()) ) if mutstring.startswith("%=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorAssignBitwiseAND()) ) if mutstring.startswith("&=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorAssignBitwiseOR()) ) if mutstring.startswith("|=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorAssignBitwiseXOR()) ) if mutstring.startswith("^=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorLeftShift()) ) if mutstring.startswith("<<") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorRightShift()) ) if mutstring.startswith(">>") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorEqual()) ) if mutstring.startswith("==") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorNotEqual()) ) if mutstring.startswith("!=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorLesserThanOrEqual()) ) if mutstring.startswith("<=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorGreaterThanOrEqual()) ) if mutstring.startswith(">=") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorLogicalAND()) ) if mutstring.startswith("&&") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorLogicalOR()) ) if mutstring.startswith("||") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorIncrement()) ) if mutstring.startswith("++") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorDecrement()) ) if mutstring.startswith("--") else \ + ( mutstring.pop_front(2), self.append(SpecialOperatorArraySubscript()) ) if mutstring.startswith("[]") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorBitwiseNOT()) ) if mutstring.startswith("~") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorAdd()) ) if mutstring.startswith("+") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorSubtract()) ) if mutstring.startswith("-") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorMultiply()) ) if mutstring.startswith("*") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorDivide()) ) if mutstring.startswith("/") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorModulo()) ) if mutstring.startswith("%") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorBitwiseAND()) ) if mutstring.startswith("&") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorBitwiseOR()) ) if mutstring.startswith("|") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorBitwiseXOR()) ) if mutstring.startswith("^") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorAssign()) ) if mutstring.startswith("=") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorLesserThan()) ) if mutstring.startswith("<") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorGreaterThan()) ) if mutstring.startswith(">") else \ + ( mutstring.pop_front(1), self.append(SpecialOperatorLogicalNOT()) ) if mutstring.startswith("!") else (): + continue + + # new, delete, new[], delete[], and co_await + curr_string = "" + while mutstring: + if mutstring[0] not in alphanumeric and mutstring[0] != '$': # '$' is for special tokens + break + curr_string += mutstring.pop(0) + if curr_string == "new": + while mutstring[0] in whitespace: + mutstring.pop(0) + ( mutstring.pop_front(2), self.append(SpecialOperatorNewArray()) ) if mutstring.startswith("[]") else self.append(SpecialOperatorNew()) + continue + if curr_string == "delete": + while mutstring[0] in whitespace: + mutstring.pop(0) + ( mutstring.pop_front(2), self.append(SpecialOperatorDeleteArray()) ) if mutstring.startswith("[]") else self.append(SpecialOperatorDelete()) + continue + if curr_string == "co_await": + self.append(SpecialOperatorCoAwait()) + continue + # uh oh + raise Exception("Special operator parsing failed!") + + if curr_string: + self.append(SyntaxToken(curr_string)) + continue + else: + raise Exception("Expression parsing failed!") +# print(self) # uncomment this to see the expression before operator resolving + # Order of Operations + i = 0 + while i < len(self): + iter = self[i] + if type(iter) == OperatorTemplateArgs: + iter.lhand = self.pop(i-1) + continue + if type(iter) == SpecialOperatorArraySubscript: + iter.lhand = self.pop(i-1) + continue + i += 1 + continue + i = 0 + while i < len(self): + iter = self[i] + if type(iter) == OperatorNamespace: + iter.rhand = self.pop(i+1) + iter.lhand = self.pop(i-1) + continue + if type(iter) == SpecialTokenUnary: + if type(self[i-1]) == SpecialOperatorAdd: + self[i-1] = SpecialOperatorPositive() + self.pop(i) + continue + if type(self[i-1]) == SpecialOperatorSubtract: + self[i-1] = SpecialOperatorNegative() + self.pop(i) + continue + if type(self[i-1]) == SpecialOperatorBitwiseAND: + self[i-1] = SpecialOperatorReference() + self.pop(i) + continue + if type(self[i-1]) == SpecialOperatorMultiply: + self[i-1] = SpecialOperatorDereference() + self.pop(i) + continue + raise Exception("Special Token \"$$unary\" can't be used for this!") + i += 1 + continue + i = len(self) - 1 + while i >= 0: + iter = self[i] + if type(iter) == OperatorLong: + iter.rhand = self.pop(i+1) + if type(iter) == OperatorUnsigned: + iter.rhand = self.pop(i+1) + if type(iter) == OperatorSigned: + iter.rhand = self.pop(i+1) + i -= 1 + i = 0 + while i < len(self): + iter = self[i] + if type(iter) == OperatorPointer: + iter.lhand = self.pop(i-1) + continue + if type(iter) == OperatorReference: + iter.lhand = self.pop(i-1) + continue + if type(iter) == OperatorRHandReference: + iter.lhand = self.pop(i-1) + continue + if type(iter) == OperatorConst: + if type(self[i-1]) == OperatorReference: + raise Exception("\'const\' qualifiers cannot be applied to references") + if type(self[i-1]) == OperatorConst: + raise Exception("Duplicate \'const\' qualifiers") + iter.lhand = self.pop(i-1) + continue + i += 1 + continue + + def __str__(self): + return " ".join(str(iter) for iter in self) + +class Signature(Expression): + def itanium_mangle(self, compressibles = None): + if compressibles == None: # This is for debugging only + compressibles = ItaniumSymbolDictionary() + + if len(self) == 0: + return "" + if len(self) == 1: + # Special signature (vtable, rtti, etc.) + return "_Z" + self[0].itanium_mangle(compressibles) + if len(self) == 2: + # Special function signature (ctors and dtors) + if type(self[1]) == OperatorFunctionArgs \ + or type(self[1]) == OperatorConst and type(self[1].lhand) == OperatorFunctionArgs: + name = self[0].itanium_mangle(compressibles) + args = self[1].itanium_mangle(compressibles) + return "_Z" + name + args + # Global variable signature + else: + name = self[1].itanium_mangle(compressibles) + if type(self[0]) == OperatorConst and type(self[0].lhand) in indirection_decorators: + name = "L" + name + return "_Z" + name + if len(self) == 3: + # Function signature + if type(self[2]) == OperatorFunctionArgs: + name = self[1].itanium_mangle(compressibles) + args = self[2].itanium_mangle(compressibles) + return "_Z" + name + args + # Const class methods + if type(self[2]) == OperatorConst and type(self[2].lhand) == OperatorFunctionArgs: + name = "NK" + self[1].itanium_mangle_ns(compressibles) + "E" # This will always be a namespace, unless it is invalid C++. + args = self[2].itanium_mangle(compressibles) + return "_Z" + name + args + raise Exception("Too much stuff!") + + +# Some stuff + +alphanumeric = ('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','1','2','3','4','5','6','7','8','9','0','_','~',) +whitespace = (' ', ' ',) +builtin_types = ( + BuiltinVoid, + BuiltinBool, + BuiltinChar, + BuiltinShort, + BuiltinInt, + Builtin__int64, + Builtin__int128, + BuiltinFloat, + BuiltinDouble, + Builtin__float80, + Builtin__float128, + BuiltinEllipses, + OperatorUnsigned, + OperatorSigned, + OperatorLong, +) +indirection_decorators = ( + OperatorPointer, + OperatorReference, + OperatorRHandReference, +) + +class ItaniumSymbolDictionary(dict): + def __init__(self): + self["std"] = "St" + self["std :: nullptr_t"] = "Dn" + self["std :: allocator"] = "Sa" + self["std :: basic_string"] = "Sb" + self["std :: basic_string < char, std :: char_traits < char >, std :: allocator < char > >"] = "Ss" + self["std :: basic_istream < char, std :: char_traits < char > >"] = "Si" + self["std :: basic_ostream < char, std :: char_traits < char > >"] = "So" + self["std :: basic_iostream < char, std :: char_traits < char > >"] = "Sd" + self.n = -1 + + def __str__(self): + for iter in self: + return "\n".join("{} : {}".format(iter, self[iter]) for iter in self) + + base_36 = { + 0 : "0", + 1 : "1", + 2 : "2", + 3 : "3", + 4 : "4", + 5 : "5", + 6 : "6", + 7 : "7", + 8 : "8", + 9 : "9", + 10 : "A", + 11 : "B", + 12 : "C", + 13 : "D", + 14 : "E", + 15 : "F", + 16 : "G", + 17 : "H", + 18 : "I", + 19 : "J", + 20 : "K", + 21 : "L", + 22 : "M", + 23 : "N", + 24 : "O", + 25 : "P", + 26 : "Q", + 27 : "R", + 28 : "S", + 29 : "T", + 30 : "U", + 31 : "V", + 32 : "W", + 33 : "X", + 34 : "Y", + 35 : "Z" + } + + def register(self, key): + if key not in self: + if self.n == -1: + val = "S_" + self.n = 0 + elif self.n == 0: + val = "S0_" + self.n = 1 + else: + idx = self.n + self.n += 1 + val = "" + while idx > 0: + val = ItaniumSymbolDictionary.base_36[idx % 36] + val + idx //= 36 + val = "S" + val + "_" + self[key] = val + +# Accessible for external use + +class ABI(Enum): + Itanium = 0 + +class LDPlusPlus(object): + def __init__(self, abi): + self.buffer = "" + self.abi = abi + + # https://sourceware.org/binutils/docs/ld/Simple-Assignments.html + def assign(self, prototype, value): + if self.abi == ABI.Itanium: + self.buffer += "{} = {};\n".format(itanium_mangle(prototype), hex(value)) + else: + raise Exception("Unsupported ABI!") + + # https://sourceware.org/binutils/docs/ld/PROVIDE.html + def provide(self, prototype, value): + if self.abi == ABI.Itanium: + self.buffer += "PROVIDE({} = {});\n".format(itanium_mangle(prototype), hex(value)) + else: + raise Exception("Unsupported ABI!") + + def save(self, filepath): + try: + with open(filepath, "w") as f: + f.write(self.buffer) + except OSError: + print("Warning: \"{:s}\" could not be opened!".format(repr(filepath)[+1:-1])) + +def mangle(prototype, abi): + if abi == ABI.Itanium: + return itanium_mangle(prototype, compressibles) + else: + raise Exception("Unsupported ABI!") + +def itanium_mangle(prototype): + return Signature(MutableString(prototype)).itanium_mangle() + +# Some diagnostics +def diagnose(prototype, correct_mangled, verbose = False): + compressibles = ItaniumSymbolDictionary() + signature = Signature(MutableString(prototype)) + mangled = signature.itanium_mangle(compressibles) + if verbose == True: + print() + print(compressibles) + print("{:50s} {:30s} {} {:30s}".format(prototype, mangled, "==" if mangled == correct_mangled else "!=",correct_mangled)) +# print("{:50s} {:30s} {} {:30s}".format(str(signature), mangled, "==" if mangled == correct_mangled else "!=",correct_mangled)) + +if __name__ == "__main__": + print("\nConst or nested variable declaration") + diagnose("int* const bar", "_ZL3bar") + diagnose("int a::bar", "_ZN1a3barE") + diagnose("int std::bar", "_ZSt3bar") + print("\nFunction declaration") + diagnose("void foo()", "_Z3foov") + print("\nDeclaration and user defined type encoding") + diagnose("void a::S::foo()", "_ZN1a1S3fooEv") + diagnose("void a::S::const_foo() const", "_ZNK1a1S9const_fooEv") # Const OperatorFunctionArgs are stupid + print("\nSubstitutions") + diagnose("void foo(void *, void *)", "_Z3fooPvS_") + print("\nMore on substitutions in function parameters") + diagnose("void foo(int)", "_Z3fooi") + diagnose("void foo()", "_Z3foov") + diagnose("void foo(void)", "_Z3foov") + diagnose("void foo(char, int, short)", "_Z3foocis") + print("\nIndirection decorators") + diagnose("void foo(int)", "_Z3fooi") + diagnose("void foo(int const)", "_Z3fooi") + diagnose("void foo(int const *)", "_Z3fooPKi") + diagnose("void foo(int const &)", "_Z3fooRKi") + diagnose("void foo(int const * const *)", "_Z3fooPKPKi") + diagnose("void foo(int * &)", "_Z3fooRPi") + print("\nFunction pointers") + print("--Massive TODO--") + print("\nMore on substitutions in scopes") + diagnose("void a::foo(a::A)", "_ZN1a3fooENS_1AE") + diagnose("void std::foo(std::A)", "_ZSt3fooSt1A") + diagnose("void A::foo(A::B)", "_ZN1A3fooENS_1BE") # Reduntant since classes are fancy namespaces + print("\nMore on substitutions in templates") + diagnose("void A::foo(A)", "_ZN1AIiE3fooES0_") + diagnose("void A::foo(int, int)", "_ZN1A3fooIiEEvT_S1_") # I don't understand this + diagnose("void A::foo(int, int)", "_ZN1AIiE3fooEii") + diagnose("void A::foo(B, B)", "_ZN1AI1BE3fooES0_S0_") + diagnose("int foo(char, int, char)", "_Z3fooIicET_T0_S0_S1_") # weird shit with function templates that would break the mangler as-is + diagnose("int foo(int, int, int)", "_Z3fooIiiET_T0_S0_S1_") # ditto + print("\nctors and dtors") + diagnose("ClassA::$$ctor()", "_ZN6ClassAC1Ev") + diagnose("ClassA::$$ctor1()", "_ZN6ClassAC1Ev") + diagnose("ClassA::$$ctor2()", "_ZN6ClassAC2Ev") + diagnose("ClassA::$$ctor3()", "_ZN6ClassAC3Ev") + diagnose("ClassA::$$dtor()", "_ZN6ClassAD1Ev") + diagnose("ClassA::$$dtor0()", "_ZN6ClassAD0Ev") + diagnose("ClassA::$$dtor1()", "_ZN6ClassAD1Ev") + diagnose("ClassA::$$dtor2()", "_ZN6ClassAD2Ev") + print("\nvtables and rtti") + diagnose("ClassA::$$vtable", "_ZTV6ClassA") + diagnose("NamespaceA::ClassA::$$vtable", "_ZTVN10NamespaceA6ClassAE") + diagnose("ClassA::$$vtt_structure", "?") + diagnose("ClassA::$$rtti", "?") + diagnose("ClassA::$$rtti_name", "?") + print("\noperator overloads") + diagnose("void* operator new(unsigned int)", "_Znwj") + diagnose("void* operator new[](unsigned int)", "_Znaj") + diagnose("void operator +(unsigned int)", "?") + diagnose("void operator + $$unary(unsigned int)", "?") + diagnose("void operator <=>(unsigned int)", "?") + diagnose("void operator >>=(unsigned int)", "?") + diagnose("void myclass::operator >>=(unsigned int)", "?") + print("\njust for fun") + diagnose("int foo::bar(MyClass arg1)", "?") +