mirror of
https://github.com/zeldaret/tp
synced 2026-08-22 06:44:53 -04:00
demangle data-symbols
This commit is contained in:
+44
-45
@@ -8,29 +8,33 @@ and apply some fixes with makes it easier to decompile.
|
||||
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import click
|
||||
from libdol2asm import settings
|
||||
import libelf
|
||||
import libar
|
||||
from pathlib import Path
|
||||
import io
|
||||
import sys
|
||||
import os
|
||||
VERSION = "1.0"
|
||||
|
||||
import os
|
||||
import sys
|
||||
import io
|
||||
from pathlib import Path
|
||||
|
||||
# laod the symbol definition file for main.dol
|
||||
sys.path.append('defs')
|
||||
import libar
|
||||
import libelf
|
||||
|
||||
|
||||
def lcf_generate(output_path):
|
||||
""" Script for generating .lcf files """
|
||||
|
||||
import module0
|
||||
|
||||
|
||||
# load symbols from compiled files
|
||||
symbols = []
|
||||
for archive in ARCHIVES:
|
||||
symbols.extend(load_archive(archive))
|
||||
|
||||
# load object files from the 'build/o_files', this way we need no list of
|
||||
# load object files from the 'build/o_files', this way we need no list of
|
||||
# object files in the python code.
|
||||
with open("build/o_files", 'r') as content_file:
|
||||
o_files = content_file.read().strip().split(" ")
|
||||
@@ -53,10 +57,12 @@ def lcf_generate(output_path):
|
||||
for name, align in SECTIONS:
|
||||
file.write("\t\t%s ALIGN(0x%X):{}\n" % (name, align))
|
||||
|
||||
# strip .dead section
|
||||
file.write("\t\t/DISCARD/ : { *(.dead) }\n")
|
||||
|
||||
file.write("\t} > text\n")
|
||||
file.write("\t_stack_addr = (_f_sbss2 + SIZEOF(.sbss2) + 65536 + 0x7) & ~0x7;\n")
|
||||
file.write(
|
||||
"\t_stack_addr = (_f_sbss2 + SIZEOF(.sbss2) + 65536 + 0x7) & ~0x7;\n")
|
||||
file.write("\t_stack_end = _f_sbss2 + SIZEOF(.sbss2);\n")
|
||||
file.write("\t_db_stack_addr = (_stack_addr + 0x2000);\n")
|
||||
file.write("\t_db_stack_end = _stack_addr;\n")
|
||||
@@ -65,28 +71,28 @@ def lcf_generate(output_path):
|
||||
file.write("\n")
|
||||
file.write("\t/* missing symbols */\n")
|
||||
|
||||
# improve decompilation workflow by making so that function
|
||||
# improve decompilation workflow by making so that function
|
||||
# which, for what ever reason, cannot be named the same as
|
||||
# the expected name to work. This will happen for all symbols
|
||||
# with weird characters.
|
||||
# the expected name to work. This will happen for all symbols
|
||||
# with weird characters.
|
||||
base_names = set(module0.SYMBOL_NAMES.keys())
|
||||
main_names = set([sym.name for sym in symbols])
|
||||
names = base_names - main_names
|
||||
for name in names:
|
||||
symbol = module0.SYMBOLS[module0.SYMBOL_NAMES[name]]
|
||||
if symbol['type'] == "StringBase": # @stringBase0 is handled below
|
||||
if symbol['type'] == "StringBase": # @stringBase0 is handled below
|
||||
continue
|
||||
if symbol['type'] == "LinkerGenerated": # linker handles these symbols
|
||||
if symbol['type'] == "LinkerGenerated": # linker handles these symbols
|
||||
continue
|
||||
|
||||
file.write(f"\t\"{symbol['label']}\" = 0x{symbol['addr']:08X};\n")
|
||||
file.write("\n")
|
||||
|
||||
# @stringBase0 is generated by the compiler. The dol2asm is using a trick to
|
||||
# @stringBase0 is generated by the compiler. The dol2asm is using a trick to
|
||||
# simulate the stringBase0 by creating another symbol (at the same location)
|
||||
# that is used instead, as it is impossible to reference the "@stringBase0" (because of the @).
|
||||
# So all references will be to the new symbol, thus the linker will think
|
||||
# that the @stringBase0 symbol is never used and strip it.
|
||||
# that the @stringBase0 symbol is never used and strip it.
|
||||
file.write("\t/* @stringBase0 */\n")
|
||||
for x in module0.SYMBOLS:
|
||||
if x['type'] == "StringBase":
|
||||
@@ -107,7 +113,7 @@ def lcf_generate(output_path):
|
||||
continue
|
||||
|
||||
require_force_active = False
|
||||
|
||||
|
||||
# if the symbol is not reachable from the __start add it as forceactive
|
||||
if not x['is_reachable'] or sum(x['r']) == 0:
|
||||
require_force_active = True
|
||||
@@ -122,7 +128,7 @@ def lcf_generate(output_path):
|
||||
continue
|
||||
|
||||
if x['is_reachable']:
|
||||
if x['label'] != x['name']:
|
||||
if x['label'] != x['name']:
|
||||
file.write(f"\t\"{x['name']}\"\n")
|
||||
|
||||
for symbol in symbols:
|
||||
@@ -136,15 +142,14 @@ def lcf_generate(output_path):
|
||||
file.write("}\n")
|
||||
file.write("\n")
|
||||
|
||||
import importlib
|
||||
from libdol2asm import settings
|
||||
|
||||
def rel_lcf_generate(module_index, output_path):
|
||||
|
||||
module = importlib.import_module(f"module{module_index}")
|
||||
base = settings.REL_TEMP_LOCATION[module.LIBRARIES[0].split("/")[-1] + ".rel"]
|
||||
base = settings.REL_TEMP_LOCATION[module.LIBRARIES[0].split(
|
||||
"/")[-1] + ".rel"]
|
||||
|
||||
# load object files from the 'build/o_files', this way we need no list of
|
||||
# load object files from the 'build/o_files', this way we need no list of
|
||||
# object files in the python code.
|
||||
with open(f"build/M{module_index}_ofiles", 'r') as content_file:
|
||||
all_files = content_file.read().strip().split(" ")
|
||||
@@ -173,7 +178,6 @@ def rel_lcf_generate(module_index, output_path):
|
||||
obj = libelf.load_object_from_file(None, o_file, file)
|
||||
symbols.extend(get_symbols_from_object_file(obj))
|
||||
|
||||
|
||||
# write rel ldscript file
|
||||
with output_path.open("w") as file:
|
||||
file.write("SECTIONS {\n")
|
||||
@@ -189,33 +193,24 @@ def rel_lcf_generate(module_index, output_path):
|
||||
file.write("\n")
|
||||
file.write("\t/* missing symbols */\n")
|
||||
|
||||
# improve decompilation workflow by making so that function
|
||||
# improve decompilation workflow by making so that function
|
||||
# which, for what ever reason, cannot be named the same as
|
||||
# the expected name to work. This will happen for all symbols
|
||||
# with weird characters.
|
||||
# the expected name to work. This will happen for all symbols
|
||||
# with weird characters.
|
||||
base_names = set(module.SYMBOL_NAMES.keys())
|
||||
main_names = set([sym.name for sym in symbols])
|
||||
names = base_names - main_names
|
||||
for name in names:
|
||||
symbol = module.SYMBOLS[module.SYMBOL_NAMES[name]]
|
||||
if symbol['type'] == "StringBase": # @stringBase0 is handled below
|
||||
if symbol['type'] == "StringBase": # @stringBase0 is handled below
|
||||
continue
|
||||
if symbol['type'] == "LinkerGenerated": # linker handles these symbols
|
||||
if symbol['type'] == "LinkerGenerated": # linker handles these symbols
|
||||
continue
|
||||
|
||||
file.write(f"\t\"{symbol['label']}\" = __rel_base + 0x{symbol['addr'] - base:08X}; /* 0x{symbol['addr']:08X} */\n")
|
||||
file.write(
|
||||
f"\t\"{symbol['label']}\" = __rel_base + 0x{symbol['addr'] - base:08X}; /* 0x{symbol['addr']:08X} */\n")
|
||||
file.write("\n")
|
||||
|
||||
# @stringBase0 is generated by the compiler. The dol2asm is using a trick to
|
||||
# simulate the stringBase0 by creating another symbol (at the same location)
|
||||
# that is used instead, as it is impossible to reference the "@stringBase0" (because of the @).
|
||||
# So all references will be to the new symbol, thus the linker will think
|
||||
# that the @stringBase0 symbol is never used and strip it.
|
||||
#file.write("\t/* @stringBase0 */\n")
|
||||
#for x in module.SYMBOLS:
|
||||
# if x['type'] == "StringBase":
|
||||
# file.write(f"\t\"{x['label']}\" = __rel_base + 0x{x['addr'] - base:08X}; /* 0x{x['addr']:08X} */\n")
|
||||
|
||||
file.write("}\n")
|
||||
file.write("\n")
|
||||
|
||||
@@ -232,7 +227,7 @@ def rel_lcf_generate(module_index, output_path):
|
||||
continue
|
||||
|
||||
require_force_active = False
|
||||
|
||||
|
||||
# if the symbol is not reachable from the __start add it as forceactive
|
||||
if not x['is_reachable'] and not x['static']:
|
||||
require_force_active = True
|
||||
@@ -261,6 +256,7 @@ def rel_lcf_generate(module_index, output_path):
|
||||
file.write("}\n")
|
||||
file.write("\n")
|
||||
|
||||
|
||||
def get_symbols_from_object_file(obj):
|
||||
symbols = []
|
||||
for sym in obj.symbols:
|
||||
@@ -269,9 +265,10 @@ def get_symbols_from_object_file(obj):
|
||||
symbols.append(sym)
|
||||
return symbols
|
||||
|
||||
|
||||
def load_archive(ar_path):
|
||||
symbols = []
|
||||
|
||||
|
||||
print(ar_path)
|
||||
archive = libar.read(ar_path)
|
||||
for path, data in archive.files:
|
||||
@@ -280,6 +277,7 @@ def load_archive(ar_path):
|
||||
|
||||
return symbols
|
||||
|
||||
|
||||
SECTIONS = [
|
||||
(".init", 0x20),
|
||||
("extab_", 0x20),
|
||||
@@ -361,28 +359,29 @@ ARCHIVES = [
|
||||
]
|
||||
|
||||
|
||||
import click
|
||||
|
||||
class PathPath(click.Path):
|
||||
def convert(self, value, param, ctx):
|
||||
return Path(super().convert(value, param, ctx))
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.version_option(VERSION)
|
||||
def lcf():
|
||||
pass
|
||||
|
||||
|
||||
@lcf.command(name="dol")
|
||||
@click.option('--output', '-o', 'output_path', required=False, type=PathPath(file_okay=True, dir_okay=False), default="build/dolzel2/ldscript.lcf")
|
||||
def dol(output_path):
|
||||
lcf_generate(output_path)
|
||||
|
||||
|
||||
@lcf.command(name="rel")
|
||||
@click.option('--output', '-o', 'output_path', required=False, type=PathPath(file_okay=True, dir_okay=False), default="build/dolzel2/ldscript.lcf")
|
||||
@click.argument('module', metavar="<MODULE>", nargs=1)
|
||||
def rel(output_path, module):
|
||||
rel_lcf_generate(module, output_path)
|
||||
rel_lcf_generate(module, output_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
lcf()
|
||||
|
||||
|
||||
@@ -39,6 +39,17 @@ class ArbitraryData(Symbol):
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_class_symbol(self):
|
||||
# @!game
|
||||
# don't generate static class variables for 'cNullVec__6Z2Calc', because it will not compile.
|
||||
# Z2Calc::cNullVec seems to be static data that is initialized in the class definition, thus,
|
||||
# every translation unit which uses Z2Calc will have a copy of the Z2Calc::cNullVec in the data
|
||||
# section. Could not find a way to make this compile without easily.
|
||||
if self.identifier.name == "cNullVec__6Z2Calc":
|
||||
return False
|
||||
return self.demangled_name and self.has_class and not self.has_template
|
||||
|
||||
@property
|
||||
def element_size(self):
|
||||
return 1
|
||||
@@ -67,18 +78,42 @@ class ArbitraryData(Symbol):
|
||||
|
||||
def array_type(self):
|
||||
if self.zero_length:
|
||||
return ZeroArrayType.create(self.element_type())
|
||||
return ZeroArrayType.create(self.element_type())
|
||||
return PaddingArrayType.create(
|
||||
self.element_type(),
|
||||
self.size // self.element_size,
|
||||
self.padding // self.element_size)
|
||||
|
||||
def cpp_reference(self, accessor, addr):
|
||||
name = self.declaration_name(forward=False, c_export=False,full_qualified_name=True)
|
||||
if addr == self.addr:
|
||||
return f"&{self.identifier.label}"
|
||||
return f"&{name}"
|
||||
else:
|
||||
offset = addr - self.addr
|
||||
return f"(((char*)&{self.identifier.label})+0x{offset:X})"
|
||||
return f"(((char*)&{name})+0x{offset:X})"
|
||||
|
||||
def declaration_name(self, forward: bool,
|
||||
c_export: bool,
|
||||
full_qualified_name: bool):
|
||||
if not self.is_class_symbol or c_export:
|
||||
return self.identifier.label
|
||||
|
||||
if full_qualified_name:
|
||||
return self.demangled_name.to_str()
|
||||
else:
|
||||
return self.demangled_name.last.to_str()
|
||||
|
||||
async def export_declaration_header(self, exporter,
|
||||
builder: AsyncBuilder,
|
||||
forward: bool,
|
||||
c_export: bool,
|
||||
full_qualified_name: bool):
|
||||
name = self.declaration_name(c_export=c_export,
|
||||
forward=forward,
|
||||
full_qualified_name=full_qualified_name)
|
||||
|
||||
decl_type = self.array_type()
|
||||
await builder.write_nonewline(decl_type.decl(name))
|
||||
|
||||
async def export_forward_references(self,
|
||||
exporter,
|
||||
@@ -86,54 +121,53 @@ class ArbitraryData(Symbol):
|
||||
c_export: bool = False):
|
||||
if not c_export:
|
||||
return
|
||||
|
||||
if self.is_static and self.export_static:
|
||||
if not self.require_forward_reference:
|
||||
return
|
||||
|
||||
if not self.is_class_symbol:
|
||||
if self.is_static and self.export_static:
|
||||
if not self.require_forward_reference:
|
||||
return
|
||||
|
||||
await self.export_section_header(builder)
|
||||
|
||||
if not (self.is_static and self.export_static):
|
||||
await self.export_extern(builder)
|
||||
if not self.is_class_symbol:
|
||||
if not (self.is_static and self.export_static):
|
||||
await self.export_extern(builder)
|
||||
|
||||
name = self.identifier.label
|
||||
if self.demangled_name:
|
||||
name = self.demangled_name.to_str(specialize_templates=False,
|
||||
without_template=False)
|
||||
|
||||
decl_type = self.array_type()
|
||||
await builder.write_nonewline(decl_type.decl(name))
|
||||
await self.export_declaration_header(exporter, builder,
|
||||
forward=True,
|
||||
c_export=c_export,
|
||||
full_qualified_name=False)
|
||||
await builder.write(";")
|
||||
|
||||
async def export_declaration_head(self, exporter, builder: AsyncBuilder):
|
||||
if self.demangled_name:
|
||||
name = self.demangled_name.to_str(specialize_templates=False,
|
||||
without_template=False)
|
||||
else:
|
||||
name = self.identifier.label
|
||||
name = self.declaration_name(c_export=False,
|
||||
forward=False,
|
||||
full_qualified_name=True)
|
||||
|
||||
decl_type = self.array_type()
|
||||
|
||||
# for empty symbols that should be exported, we need to double declare it.
|
||||
# otherwise, the compiler thinks that we're not actual declaring it.
|
||||
is_extern = not (self.is_static and self.export_as_static)
|
||||
if not self.data and is_extern:
|
||||
await self.export_section(builder)
|
||||
if self.force_section:
|
||||
await self.export_section_header(builder)
|
||||
if not self.is_class_symbol:
|
||||
# for empty symbols that should be exported, we need to double declare it.
|
||||
# otherwise, the compiler thinks that we're not actual declaring it.
|
||||
is_extern = not (self.is_static and self.export_as_static)
|
||||
if not self.data and is_extern:
|
||||
await self.export_section(builder)
|
||||
if self.force_section:
|
||||
await self.export_section_header(builder)
|
||||
|
||||
await self.export_extern(builder)
|
||||
await builder.write_nonewline(decl_type.decl(name))
|
||||
await builder.write(";")
|
||||
await self.export_extern(builder)
|
||||
await builder.write_nonewline(decl_type.decl(name))
|
||||
await builder.write(";")
|
||||
|
||||
await self.export_section(builder)
|
||||
if self.force_section:
|
||||
await self.export_section_header(builder)
|
||||
|
||||
if not is_extern:
|
||||
await self.export_static(builder)
|
||||
elif self.data and is_extern:
|
||||
await self.export_extern(builder)
|
||||
if not self.is_class_symbol:
|
||||
if not is_extern:
|
||||
await self.export_static(builder)
|
||||
elif self.data and is_extern:
|
||||
await self.export_extern(builder)
|
||||
|
||||
await builder.write_nonewline(decl_type.decl(name))
|
||||
|
||||
@@ -168,11 +202,11 @@ class ArbitraryData(Symbol):
|
||||
|
||||
if self._section == ".rodata":
|
||||
await builder.write(f"COMPILER_STRIP_GATE({self.addr:08X}, {self.cpp_reference(None, self.addr)});")
|
||||
#await builder.write_nonewline("SECTION_DEAD ")
|
||||
#await builder.write_nonewline("void* const ")
|
||||
#await builder.write_nonewline(f"cg_{self.addr:08X} = (void*)(")
|
||||
#await builder.write_nonewline(self.cpp_reference(None, self.addr))
|
||||
#await builder.write(f");")
|
||||
# await builder.write_nonewline("SECTION_DEAD ")
|
||||
# await builder.write_nonewline("void* const ")
|
||||
# await builder.write_nonewline(f"cg_{self.addr:08X} = (void*)(")
|
||||
# await builder.write_nonewline(self.cpp_reference(None, self.addr))
|
||||
# await builder.write(f");")
|
||||
|
||||
if self.requires_force_active:
|
||||
await builder.write(f"#pragma pop")
|
||||
|
||||
@@ -18,15 +18,13 @@ special_func_no_return = set([
|
||||
class Function(Symbol):
|
||||
return_type: Type = None
|
||||
argument_types: List[Type] = field(default_factory=list)
|
||||
func_name: libdemangle.QualifiedName = None
|
||||
special_func_name: str = None
|
||||
func_is_const: bool = False
|
||||
template_index: int = -1
|
||||
asm: bool = False
|
||||
|
||||
@property
|
||||
def uses_any_templates(self):
|
||||
if self.func_name and self.func_name.has_template:
|
||||
if self.demangled_name and self.demangled_name.has_template:
|
||||
return True
|
||||
|
||||
is_templated = [False]
|
||||
@@ -44,10 +42,6 @@ class Function(Symbol):
|
||||
|
||||
return is_templated[0]
|
||||
|
||||
@property
|
||||
def uses_class_template(self):
|
||||
return self.func_name and self.func_name.has_template
|
||||
|
||||
@property
|
||||
def is_static(self):
|
||||
s = self.reference_count.static
|
||||
@@ -57,7 +51,7 @@ class Function(Symbol):
|
||||
if not static_by_references:
|
||||
return False
|
||||
|
||||
if not self.func_name:
|
||||
if not self.demangled_name:
|
||||
# very arbitrary, but function begining with __ are often special
|
||||
if self.identifier.name and self.identifier.name.startswith("__"):
|
||||
return False
|
||||
@@ -70,13 +64,13 @@ class Function(Symbol):
|
||||
return self.identifier.label
|
||||
|
||||
def function_name(self, c_export: bool, full_qualified_name: bool):
|
||||
if not self.func_name or c_export:
|
||||
if not self.demangled_name or c_export:
|
||||
return self.identifier.label
|
||||
|
||||
if self.func_name.require_specialization:
|
||||
if self.demangled_name.require_specialization:
|
||||
return self.identifier.label
|
||||
|
||||
name = self.func_name
|
||||
name = self.demangled_name
|
||||
if self.special_func_name and self.has_class:
|
||||
# fix up the constructor and destructor if the function is template specialized
|
||||
special_name = None
|
||||
@@ -87,16 +81,13 @@ class Function(Symbol):
|
||||
|
||||
if special_name:
|
||||
name = NamedType(
|
||||
self.func_name.names[:-1] + [ClassName(special_name, [])])
|
||||
self.demangled_name.names[:-1] + [ClassName(special_name, [])])
|
||||
|
||||
if full_qualified_name:
|
||||
return name.to_str()
|
||||
else:
|
||||
return name.last.to_str()
|
||||
|
||||
def is_demangled(self):
|
||||
return self.func_name != None
|
||||
|
||||
def valid_reference(self, addr):
|
||||
return addr % 4 == 0
|
||||
|
||||
@@ -115,14 +106,6 @@ class Function(Symbol):
|
||||
def types(self):
|
||||
return set()
|
||||
|
||||
@property
|
||||
def has_class(self):
|
||||
return self.func_name and self.func_name.has_class
|
||||
|
||||
@property
|
||||
def has_template(self):
|
||||
return self.func_name and self.func_name.has_template
|
||||
|
||||
async def export_function_header(self, exporter,
|
||||
builder: AsyncBuilder,
|
||||
forward: bool,
|
||||
@@ -158,7 +141,7 @@ class Function(Symbol):
|
||||
|
||||
if self._section == ".init":
|
||||
await builder.write_nonewline(f"SECTION_INIT ")
|
||||
elif c_export or (self.func_name and self.func_name.require_specialization and full_qualified_name):
|
||||
elif c_export or (self.demangled_name and self.demangled_name.require_specialization and full_qualified_name):
|
||||
await builder.write_nonewline(f"extern \"C\" ")
|
||||
|
||||
if self.is_static and not self.has_class:
|
||||
@@ -173,7 +156,7 @@ class Function(Symbol):
|
||||
is_special_function = (
|
||||
self.special_func_name in special_func_no_return)
|
||||
specialized = (
|
||||
self.func_name and self.func_name.require_specialization)
|
||||
self.demangled_name and self.demangled_name.require_specialization)
|
||||
if c_export:
|
||||
await builder.write_nonewline(f"{return_type.type()} ")
|
||||
await builder.write_nonewline(f"{self.function_name(c_export=True,full_qualified_name=full_qualified_name)}")
|
||||
|
||||
@@ -62,6 +62,7 @@ class Symbol:
|
||||
_section: str = None
|
||||
alignment: int = 0
|
||||
relative_addr: int = -1
|
||||
template_index: int = -1
|
||||
|
||||
demangled_name: NamedType = None
|
||||
references: Set[int] = field(default_factory=set)
|
||||
@@ -100,9 +101,21 @@ class Symbol:
|
||||
def has_body(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def has_class(self):
|
||||
return self.demangled_name and self.demangled_name.has_class
|
||||
|
||||
@property
|
||||
def has_template(self):
|
||||
return self.demangled_name and self.demangled_name.has_template
|
||||
|
||||
@property
|
||||
def uses_class_template(self):
|
||||
return False
|
||||
return self.demangled_name and self.demangled_name.has_template
|
||||
|
||||
@property
|
||||
def is_demangled(self):
|
||||
return self.demangled_name != None
|
||||
|
||||
@property
|
||||
def requires_force_active(self):
|
||||
|
||||
@@ -309,6 +309,8 @@ class CPPExporter:
|
||||
for symbol in symbols:
|
||||
if isinstance(symbol, StringBase):
|
||||
continue
|
||||
if symbol in type_list.require_forward_c_reference:
|
||||
continue
|
||||
already_fixed_forward_reference.add(symbol)
|
||||
|
||||
forward_references = list(decl_references - already_fixed_forward_reference)
|
||||
|
||||
@@ -51,6 +51,7 @@ class TypeList:
|
||||
def __init__(self, context: Context):
|
||||
self.context = context
|
||||
self.global_types = dict()
|
||||
self.require_forward_c_reference = set()
|
||||
|
||||
def build(self, symbols: Set[Symbol]):
|
||||
for symbol in symbols:
|
||||
@@ -119,18 +120,22 @@ class TypeList:
|
||||
type.traverse(callback, depth=0)
|
||||
|
||||
def convert_symbol_to_types(self, symbol):
|
||||
if not isinstance(symbol, Function):
|
||||
return
|
||||
if isinstance(symbol, Function):
|
||||
struct = None
|
||||
if symbol.has_class:
|
||||
struct = self.get_or_create_type_from_name(
|
||||
None, -1, self.global_types, symbol.demangled_name.names)
|
||||
struct.symbols.append(symbol)
|
||||
|
||||
struct = None
|
||||
if symbol.has_class:
|
||||
struct = self.get_or_create_type_from_name(
|
||||
None, -1, self.global_types, symbol.func_name.names)
|
||||
struct.symbols.append(symbol)
|
||||
|
||||
self.build_type_structure(struct, symbol.return_type)
|
||||
for arg in symbol.argument_types:
|
||||
self.build_type_structure(struct, arg)
|
||||
self.build_type_structure(struct, symbol.return_type)
|
||||
for arg in symbol.argument_types:
|
||||
self.build_type_structure(struct, arg)
|
||||
elif symbol.demangled_name:
|
||||
if symbol.is_class_symbol:
|
||||
self.require_forward_c_reference.add(symbol)
|
||||
struct = self.get_or_create_type_from_name(
|
||||
None, -1, self.global_types, symbol.demangled_name.names)
|
||||
struct.symbols.append(symbol)
|
||||
|
||||
def struct_dependencies(self, struct):
|
||||
deps = set()
|
||||
@@ -213,7 +218,7 @@ class TypeList:
|
||||
if not move_function:
|
||||
await builder.write_nonewline(f"{pad}/* {function.addr:08X} */ ")
|
||||
if function.template_index >= 0:
|
||||
await builder.write(f"/* {function.func_name.to_str()} */")
|
||||
await builder.write(f"/* {function.demangled_name.to_str()} */")
|
||||
await builder.write_nonewline(f"{pad}")
|
||||
|
||||
await function.export_function_header(self, builder,
|
||||
@@ -222,6 +227,24 @@ class TypeList:
|
||||
full_qualified_name=False)
|
||||
await builder.write(f";")
|
||||
|
||||
async def export_struct_symbol(self, builder, parent, indent, symbol, specialize):
|
||||
pad = "\t" * indent
|
||||
await builder.write_nonewline(f"{pad}")
|
||||
|
||||
# non-function symbols will always be static inside structs
|
||||
if isinstance(parent, Struct):
|
||||
await builder.write_nonewline(f"static ")
|
||||
|
||||
if symbol.template_index >= 0:
|
||||
await builder.write(f"/* {symbol.demangled_name.to_str()} */")
|
||||
await builder.write_nonewline(f"{pad}")
|
||||
|
||||
await symbol.export_declaration_header(self, builder,
|
||||
forward=True,
|
||||
c_export=False,
|
||||
full_qualified_name=False)
|
||||
await builder.write(f";")
|
||||
|
||||
async def export_struct(self, builder, struct_index, struct, indent):
|
||||
self.struct_export_set.add(struct)
|
||||
|
||||
@@ -243,14 +266,23 @@ class TypeList:
|
||||
# group functions by name (this is essential grouping templated functions)
|
||||
names = defaultdict(list)
|
||||
for symbol in set(struct.symbols):
|
||||
names[symbol.func_name.last.name].append(symbol)
|
||||
names[symbol.demangled_name.last.name].append(symbol)
|
||||
|
||||
symbols = list(names.items())
|
||||
symbols.sort(key=lambda x: min([z.addr for z in x[1]]))
|
||||
|
||||
fsymbols = []
|
||||
ssymbols = []
|
||||
|
||||
for name, functions in symbols:
|
||||
if isinstance(functions[0], Function):
|
||||
fsymbols.append((name, functions))
|
||||
else:
|
||||
ssymbols.append((name, functions))
|
||||
|
||||
# export functions
|
||||
last_template = False
|
||||
for name, functions in symbols:
|
||||
for name, functions in fsymbols:
|
||||
if last_template:
|
||||
await builder.write(f"")
|
||||
last_template = False
|
||||
@@ -258,28 +290,39 @@ class TypeList:
|
||||
if functions[0].template_index >= 0:
|
||||
# export templated functions
|
||||
first_function = functions[0]
|
||||
assert isinstance(first_function, Function)
|
||||
move_function = self.function_requires_move(first_function)
|
||||
|
||||
# export generic function header
|
||||
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
args = [
|
||||
f"{alphabet[i]}{struct.depth+1}"
|
||||
for i in range(len(first_function.func_name.last.templates))
|
||||
for i in range(len(first_function.demangled_name.last.templates))
|
||||
]
|
||||
typename_args = ", ".join([f"typename {x}" for x in args])
|
||||
await builder.write(f"{pad}\ttemplate <{typename_args}>")
|
||||
await builder.write_nonewline(f"{pad}\t")
|
||||
await builder.write_nonewline(f"void {first_function.func_name.last.name}(/* ... */)")
|
||||
await builder.write_nonewline(f"void {first_function.demangled_name.last.name}(/* ... */)")
|
||||
|
||||
await builder.write(f";")
|
||||
|
||||
for function in functions:
|
||||
assert isinstance(function, Function)
|
||||
await self.export_struct_function(builder, struct, indent + 1, function, True)
|
||||
last_template = True
|
||||
else:
|
||||
# export normal functions
|
||||
# export functions
|
||||
for function in functions:
|
||||
assert isinstance(function, Function)
|
||||
await self.export_struct_function(builder, struct, indent + 1, function, (struct_index >= 0))
|
||||
|
||||
if len(fsymbols) > 0 and len(ssymbols) > 0:
|
||||
await builder.write("")
|
||||
|
||||
for name, syms in ssymbols:
|
||||
symbol = syms[0]
|
||||
assert not isinstance(symbol, Function)
|
||||
await self.export_struct_symbol(builder, struct, indent + 1, symbol, (struct_index >= 0))
|
||||
|
||||
await builder.write(f"{pad}}};")
|
||||
await builder.write("")
|
||||
|
||||
+21
-18
@@ -118,8 +118,10 @@ def type_from_demangled_param(param):
|
||||
|
||||
return type
|
||||
|
||||
|
||||
dollar_re = re.compile(r'(\w+)\$([0-9]+)')
|
||||
|
||||
|
||||
def nameFix(context, label_collisions, reference_collisions, dollar_names, symbol):
|
||||
util.escape_name(symbol.identifier)
|
||||
|
||||
@@ -132,16 +134,17 @@ def nameFix(context, label_collisions, reference_collisions, dollar_names, symbo
|
||||
symbol.identifier.override_name = f"{match.group(1)}_{match.group(2)}"
|
||||
|
||||
# TODO: Support demangled names for variables
|
||||
"""
|
||||
if symbol.identifier.name and (not "@" in symbol.identifier.name) and not isinstance(symbol, Function):
|
||||
if (symbol.identifier.name and
|
||||
(not "@" in symbol.identifier.name) and
|
||||
(not "$" in symbol.identifier.name) and
|
||||
not isinstance(symbol, Function) and
|
||||
not symbol.identifier.name.startswith("__vt")):
|
||||
try:
|
||||
name = symbol.identifier.name
|
||||
p = libdemangle.ParseCtx(name)
|
||||
p.demangle_variable()
|
||||
|
||||
if len(p.to_str()) > 0 and p.to_str() != name:
|
||||
#context.debug(p.to_str())
|
||||
|
||||
types = [
|
||||
type_from_demangled_param(x)
|
||||
for x in p.demangled
|
||||
@@ -153,16 +156,16 @@ def nameFix(context, label_collisions, reference_collisions, dollar_names, symbo
|
||||
valid = False
|
||||
break
|
||||
|
||||
if valid:
|
||||
symbol.demangled_name = named_type_from_qulified_name(p.full_name)
|
||||
if valid:
|
||||
symbol.demangled_name = named_type_from_qulified_name(
|
||||
p.full_name)
|
||||
except Exception as e:
|
||||
context.error(f"demangle error: '{name}'")
|
||||
context.error(f"\t{e}")
|
||||
context.error(f"\t{p.func_name}")
|
||||
context.error(f"\t{p.class_name}")
|
||||
context.error(f"\t{p.to_str()}")
|
||||
"""
|
||||
|
||||
|
||||
if (symbol.identifier.name and (not "@" in symbol.identifier.name) and isinstance(symbol, Function)):
|
||||
name = symbol.identifier.name
|
||||
try:
|
||||
@@ -182,7 +185,7 @@ def nameFix(context, label_collisions, reference_collisions, dollar_names, symbo
|
||||
break
|
||||
|
||||
if valid:
|
||||
symbol.func_name = named_type_from_qulified_name(
|
||||
symbol.demangled_name = named_type_from_qulified_name(
|
||||
p.full_name)
|
||||
symbol.func_is_const = p.is_const
|
||||
symbol.special_func_name = p.special_func_name
|
||||
@@ -195,7 +198,7 @@ def nameFix(context, label_collisions, reference_collisions, dollar_names, symbo
|
||||
else:
|
||||
symbol.return_type = return_type
|
||||
|
||||
#if not symbol.uses_class_template:
|
||||
# if not symbol.uses_class_template:
|
||||
# symbol.identifier.is_name_safe = True
|
||||
else:
|
||||
context.warning(
|
||||
@@ -217,7 +220,8 @@ def nameFix(context, label_collisions, reference_collisions, dollar_names, symbo
|
||||
|
||||
def nameCollision(context, label_collisions, reference_collisions, parent_name, symbol):
|
||||
if not symbol.is_static or isinstance(symbol, StringBase):
|
||||
nice_name = parent_name.replace("/", "_").replace(".", "_").replace("-", "_")
|
||||
nice_name = parent_name.replace(
|
||||
"/", "_").replace(".", "_").replace("-", "_")
|
||||
if isinstance(symbol, StringBase) and symbol._module != 0:
|
||||
symbol.identifier.override_name = nice_name + "__" + symbol.identifier.label
|
||||
elif label_collisions[symbol.identifier.label] > 1 or reference_collisions[symbol.identifier.reference] > 1:
|
||||
@@ -240,7 +244,6 @@ def execute(context, libraries):
|
||||
continue
|
||||
dollar_names[match.group(1)] += 1
|
||||
|
||||
|
||||
for sec in tu.sections.values():
|
||||
for symbol in sec.symbols:
|
||||
nameFix(context, label_collisions,
|
||||
@@ -267,10 +270,10 @@ def execute(context, libraries):
|
||||
for section in tu.sections.values():
|
||||
for symbol in section.symbols:
|
||||
if isinstance(symbol, Function):
|
||||
if symbol.is_demangled():
|
||||
add_named_type(symbol.func_name, 1)
|
||||
if symbol.is_demangled:
|
||||
add_named_type(symbol.demangled_name, 1)
|
||||
raw_names = tuple(
|
||||
[x.name for x in symbol.func_name.names])
|
||||
[x.name for x in symbol.demangled_name.names])
|
||||
names[raw_names].append(symbol)
|
||||
|
||||
if symbol.return_type:
|
||||
@@ -301,10 +304,10 @@ def execute(context, libraries):
|
||||
|
||||
# generate template_index for functions.
|
||||
for name, functions in names.items():
|
||||
if functions[0].func_name.last.templates:
|
||||
if functions[0].demangled_name.last.templates:
|
||||
#context.debug("Found Templated Function(s):")
|
||||
functions.sort(key=lambda x: x.addr)
|
||||
for i, function in enumerate(functions):
|
||||
function.template_index = i
|
||||
function.func_name.last.template_index = i
|
||||
# context.debug(f"\t{function.func_name.to_str(without_template=True)}")
|
||||
function.demangled_name.last.template_index = i
|
||||
# context.debug(f"\t{function.demangled_name.to_str(without_template=True)}")
|
||||
|
||||
@@ -207,7 +207,7 @@ class Dol2AsmSplitter:
|
||||
cs.append((0, section.size))
|
||||
|
||||
executable_section = ExecutableSection(
|
||||
section.name, section.addr, section.size, 0, section.data,
|
||||
section.name, section.addr, section.size, 0, section.data,
|
||||
code_segments=cs, relocations={}, alignment=4)
|
||||
executable_sections.append(executable_section)
|
||||
|
||||
@@ -245,9 +245,9 @@ class Dol2AsmSplitter:
|
||||
|
||||
exe_section = ExecutableSection(
|
||||
section.name, section.addr, section.length, base_addr,
|
||||
section.data,
|
||||
code_segments=cs,
|
||||
relocations={},
|
||||
section.data,
|
||||
code_segments=cs,
|
||||
relocations={},
|
||||
alignment=section.alignment)
|
||||
exe_section.raw_offset = offset
|
||||
executable_sections.append(exe_section)
|
||||
@@ -628,7 +628,7 @@ class Dol2AsmSplitter:
|
||||
self.cpp_group_count = 4
|
||||
self.asm_group_count = 128
|
||||
self.step_count = 1
|
||||
cache = False
|
||||
cache = True
|
||||
|
||||
print(f"dol2asm {VERSION} for '{settings.GAME_NAME}'")
|
||||
|
||||
@@ -659,7 +659,6 @@ class Dol2AsmSplitter:
|
||||
self.search_binary(cache)
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
cache_path = Path("build/full_cache_xx.dump")
|
||||
if cache and cache_path.exists():
|
||||
with cache_path.open('rb') as input:
|
||||
@@ -694,10 +693,12 @@ class Dol2AsmSplitter:
|
||||
if not self.select_modules and self.rel_gen:
|
||||
global_destructor_chain_path = Path(__file__).parent.joinpath(
|
||||
"global_destructor_chain.template.cpp")
|
||||
executor_path = Path(__file__).parent.joinpath("executor.template.cpp")
|
||||
executor_path = Path(__file__).parent.joinpath(
|
||||
"executor.template.cpp")
|
||||
|
||||
if global_destructor_chain_path.exists():
|
||||
output_path = self.rel_path.joinpath("global_destructor_chain.cpp")
|
||||
output_path = self.rel_path.joinpath(
|
||||
"global_destructor_chain.cpp")
|
||||
util._create_dirs_for_file(output_path)
|
||||
with global_destructor_chain_path.open('r') as input:
|
||||
with output_path.open('w') as output:
|
||||
|
||||
Reference in New Issue
Block a user