diff --git a/3rdparty/ccc/CMakeLists.txt b/3rdparty/ccc/CMakeLists.txt
new file mode 100644
index 0000000000..7ecffbdca6
--- /dev/null
+++ b/3rdparty/ccc/CMakeLists.txt
@@ -0,0 +1,41 @@
+cmake_minimum_required(VERSION 3.14)
+project(ccc)
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+add_library(ccc STATIC
+ src/ccc/ast.cpp
+ src/ccc/ast.h
+ src/ccc/elf.cpp
+ src/ccc/elf.h
+ src/ccc/elf_symtab.cpp
+ src/ccc/elf_symtab.h
+ src/ccc/importer_flags.cpp
+ src/ccc/importer_flags.h
+ src/ccc/mdebug_analysis.cpp
+ src/ccc/mdebug_analysis.h
+ src/ccc/mdebug_importer.cpp
+ src/ccc/mdebug_importer.h
+ src/ccc/mdebug_section.cpp
+ src/ccc/mdebug_section.h
+ src/ccc/mdebug_symbols.cpp
+ src/ccc/mdebug_symbols.h
+ src/ccc/sndll.cpp
+ src/ccc/sndll.h
+ src/ccc/stabs.cpp
+ src/ccc/stabs.h
+ src/ccc/stabs_to_ast.cpp
+ src/ccc/stabs_to_ast.h
+ src/ccc/symbol_database.cpp
+ src/ccc/symbol_database.h
+ src/ccc/symbol_file.cpp
+ src/ccc/symbol_file.h
+ src/ccc/symbol_table.cpp
+ src/ccc/symbol_table.h
+ src/ccc/util.cpp
+ src/ccc/util.h
+)
+
+target_include_directories(ccc PUBLIC src)
diff --git a/3rdparty/ccc/README.md b/3rdparty/ccc/README.md
new file mode 100644
index 0000000000..9c8f1efecf
--- /dev/null
+++ b/3rdparty/ccc/README.md
@@ -0,0 +1,37 @@
+# Chaos Compiler Collection
+
+This code was originally developed in the following repository and was copied
+into PCSX2 by the author:
+
+- [https://github.com/chaoticgd/ccc](https://github.com/chaoticgd/ccc)
+
+It includes additional resources that are not present in the PCSX2 repository.
+
+## Documentation
+
+### DWARF (.debug) Section
+
+- [DWARF Debugging Information Format](https://dwarfstd.org/doc/dwarf_1_1_0.pdf)
+
+### MIPS Debug (.mdebug) Section
+
+- [Third Eye Software and the MIPS symbol table (Peter Rowell)](http://datahedron.com/mips.html)
+- [MIPS Mdebug Debugging Information (David Anderson, 1996)](https://www.prevanders.net/Mdebug.ps)
+- MIPS Assembly Language Programmer's Guide, Symbol Table Chapter (Silicon Graphics, 1992)
+- Tru64 UNIX Object File and Symbol Table Format Specification, Symbol Table Chapter
+- `mdebugread.c` from gdb (reading)
+- `ecoff.c` from gas (writing)
+- `include/coff/sym.h` from binutils (headers)
+
+### MIPS EABI
+
+- [MIPS EABI](https://sourceware.org/legacy-ml/binutils/2003-06/msg00436.html)
+
+### STABS
+
+- [The "stabs" representation of debugging information (Julia Menapace, Jim Kingdon, and David MacKenzie, 1992-???)](https://sourceware.org/gdb/onlinedocs/stabs.html)
+- `stabs.c` from binutils (reading)
+- `stabsread.c` from gdb (reading)
+- `dbxread.c` from gdb (reading)
+- `dbxout.c` from gcc (writing)
+- `stab.def` from gcc (symbol codes)
diff --git a/3rdparty/ccc/ccc.vcxproj b/3rdparty/ccc/ccc.vcxproj
new file mode 100644
index 0000000000..16ab210948
--- /dev/null
+++ b/3rdparty/ccc/ccc.vcxproj
@@ -0,0 +1,75 @@
+
+
+
+
+
+ {2589F8CE-EA77-4B73-911E-64074569795B}
+
+
+
+ StaticLibrary
+ $(DefaultPlatformToolset)
+ ClangCL
+ MultiByte
+ true
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+ AllRules.ruleset
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TurnOffAllWarnings
+ $(ProjectDir)src;%(AdditionalIncludeDirectories)
+ stdcpp20
+
+
+
+
+
diff --git a/3rdparty/ccc/ccc.vcxproj.filters b/3rdparty/ccc/ccc.vcxproj.filters
new file mode 100644
index 0000000000..9f49bde786
--- /dev/null
+++ b/3rdparty/ccc/ccc.vcxproj.filters
@@ -0,0 +1,111 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
diff --git a/3rdparty/ccc/src/ccc/ast.cpp b/3rdparty/ccc/src/ccc/ast.cpp
new file mode 100644
index 0000000000..fb5709d366
--- /dev/null
+++ b/3rdparty/ccc/src/ccc/ast.cpp
@@ -0,0 +1,562 @@
+// This file is part of the Chaos Compiler Collection.
+// SPDX-License-Identifier: MIT
+
+#include "ast.h"
+
+#include "importer_flags.h"
+#include "symbol_database.h"
+
+namespace ccc::ast {
+
+static bool compare_nodes_and_merge(
+ CompareResult& dest, const Node& node_lhs, const Node& node_rhs, const SymbolDatabase* database);
+static bool try_to_match_wobbly_typedefs(
+ const Node& node_lhs, const Node& node_rhs, const SymbolDatabase& database);
+
+void Node::set_access_specifier(AccessSpecifier specifier, u32 importer_flags)
+{
+ if((importer_flags & NO_ACCESS_SPECIFIERS) == 0) {
+ access_specifier = specifier;
+ }
+}
+
+std::pair Node::physical_type(SymbolDatabase& database, s32 max_depth)
+{
+ Node* type = this;
+ DataType* symbol = nullptr;
+ for(s32 i = 0; i < max_depth && type->descriptor == TYPE_NAME; i++) {
+ DataType* data_type = database.data_types.symbol_from_handle(type->as().data_type_handle);
+ if (!data_type || !data_type->type()) {
+ break;
+ }
+
+ type = data_type->type();
+ symbol = data_type;
+ }
+
+ return std::pair(type, symbol);
+}
+
+std::pair Node::physical_type(const SymbolDatabase& database, s32 max_depth) const
+{
+ return const_cast(this)->physical_type(const_cast(database), max_depth);
+}
+
+const char* member_function_modifier_to_string(MemberFunctionModifier modifier)
+{
+ switch(modifier) {
+ case MemberFunctionModifier::NONE: return "none";
+ case MemberFunctionModifier::STATIC: return "static";
+ case MemberFunctionModifier::VIRTUAL: return "virtual";
+ }
+ return "";
+}
+
+bool StructOrUnion::flatten_fields(
+ std::vector& output,
+ const DataType* symbol,
+ const SymbolDatabase& database,
+ bool skip_statics,
+ s32 base_offset,
+ s32 max_fields,
+ s32 max_depth) const
+{
+ if(max_depth == 0) {
+ return false;
+ }
+
+ for(const std::unique_ptr& type_name : base_classes) {
+ if(type_name->descriptor != TYPE_NAME) {
+ continue;
+ }
+
+ s32 new_base_offset = base_offset + type_name->offset_bytes;
+
+ DataTypeHandle handle = type_name->as().data_type_handle;
+ const DataType* base_class_symbol = database.data_types.symbol_from_handle(handle);
+ if(!base_class_symbol || !base_class_symbol->type() || base_class_symbol->type()->descriptor != STRUCT_OR_UNION) {
+ continue;
+ }
+
+ const StructOrUnion& base_class = base_class_symbol->type()->as();
+ if(!base_class.flatten_fields(output, base_class_symbol, database, skip_statics, new_base_offset, max_fields, max_depth - 1)) {
+ return false;
+ }
+ }
+
+ for(const std::unique_ptr& field : fields) {
+ if(skip_statics && field->storage_class == STORAGE_CLASS_STATIC) {
+ continue;
+ }
+
+ if((s32) output.size() >= max_fields) {
+ return false;
+ }
+
+ FlatField& flat = output.emplace_back();
+ flat.node = field.get();
+ flat.symbol = symbol;
+ flat.base_offset = base_offset;
+ }
+
+ return true;
+}
+
+const char* type_name_source_to_string(TypeNameSource source)
+{
+ switch(source) {
+ case TypeNameSource::REFERENCE: return "reference";
+ case TypeNameSource::CROSS_REFERENCE: return "cross_reference";
+ case TypeNameSource::UNNAMED_THIS: return "this";
+ }
+ return "";
+}
+
+const char* forward_declared_type_to_string(ForwardDeclaredType type)
+{
+ switch(type) {
+ case ForwardDeclaredType::STRUCT: return "struct";
+ case ForwardDeclaredType::UNION: return "union";
+ case ForwardDeclaredType::ENUM: return "enum";
+ }
+ return "";
+}
+
+DataTypeHandle TypeName::data_type_handle_unless_forward_declared() const
+{
+ if(!is_forward_declared) {
+ return data_type_handle;
+ } else {
+ return DataTypeHandle();
+ }
+}
+
+CompareResult compare_nodes(
+ const Node& node_lhs, const Node& node_rhs, const SymbolDatabase* database, bool check_intrusive_fields)
+{
+ CompareResult result = CompareResultType::MATCHES_NO_SWAP;
+
+ if(node_lhs.descriptor != node_rhs.descriptor) {
+ return CompareFailReason::DESCRIPTOR;
+ }
+
+ if(check_intrusive_fields) {
+ if(node_lhs.storage_class != node_rhs.storage_class) {
+ // In some cases we can determine that a type was typedef'd for C
+ // translation units, but not for C++ translation units, so we need
+ // to add a special case for that here.
+ if(node_lhs.storage_class == STORAGE_CLASS_TYPEDEF && node_rhs.storage_class == STORAGE_CLASS_NONE) {
+ result = CompareResultType::MATCHES_FAVOUR_LHS;
+ } else if(node_lhs.storage_class == STORAGE_CLASS_NONE && node_rhs.storage_class == STORAGE_CLASS_TYPEDEF) {
+ result = CompareResultType::MATCHES_FAVOUR_RHS;
+ } else {
+ return CompareFailReason::STORAGE_CLASS;
+ }
+ }
+
+ // Vtable pointers and constructors can sometimes contain type numbers
+ // that are different between translation units, so we don't want to
+ // compare them.
+ bool is_vtable_pointer = node_lhs.is_vtable_pointer && node_rhs.is_vtable_pointer;
+ bool is_numbered_constructor = node_lhs.name.starts_with("$_") && node_rhs.name.starts_with("$_");
+ if(node_lhs.name != node_rhs.name && !is_vtable_pointer && !is_numbered_constructor) {
+ return CompareFailReason::NAME;
+ }
+
+ if(node_lhs.offset_bytes != node_rhs.offset_bytes) {
+ return CompareFailReason::RELATIVE_OFFSET_BYTES;
+ }
+
+ if(node_lhs.size_bits != node_rhs.size_bits) {
+ return CompareFailReason::SIZE_BITS;
+ }
+
+ if(node_lhs.is_const != node_rhs.is_const) {
+ return CompareFailReason::CONSTNESS;
+ }
+ }
+
+ switch(node_lhs.descriptor) {
+ case ARRAY: {
+ const auto [lhs, rhs] = Node::as(node_lhs, node_rhs);
+
+ if(compare_nodes_and_merge(result, *lhs.element_type.get(), *rhs.element_type.get(), database)) {
+ return result;
+ }
+
+ if(lhs.element_count != rhs.element_count) {
+ return CompareFailReason::ARRAY_ELEMENT_COUNT;
+ }
+
+ break;
+ }
+ case BITFIELD: {
+ const auto [lhs, rhs] = Node::as(node_lhs, node_rhs);
+
+ if(lhs.bitfield_offset_bits != rhs.bitfield_offset_bits) {
+ return CompareFailReason::BITFIELD_OFFSET_BITS;
+ }
+
+ if(compare_nodes_and_merge(result, *lhs.underlying_type.get(), *rhs.underlying_type.get(), database)) {
+ return result;
+ }
+
+ break;
+ }
+ case BUILTIN: {
+ const auto [lhs, rhs] = Node::as(node_lhs, node_rhs);
+
+ if(lhs.bclass != rhs.bclass) {
+ return CompareFailReason::BUILTIN_CLASS;
+ }
+
+ break;
+ }
+ case ENUM: {
+ const auto [lhs, rhs] = Node::as(node_lhs, node_rhs);
+
+ if(lhs.constants != rhs.constants) {
+ return CompareFailReason::ENUM_CONSTANTS;
+ }
+
+ break;
+ }
+ case ERROR_NODE: {
+ break;
+ }
+ case FUNCTION: {
+ const auto [lhs, rhs] = Node::as(node_lhs, node_rhs);
+
+ if(lhs.return_type.has_value() != rhs.return_type.has_value()) {
+ return CompareFailReason::FUNCTION_RETURN_TYPE_HAS_VALUE;
+ }
+
+ if(lhs.return_type.has_value()) {
+ if(compare_nodes_and_merge(result, *lhs.return_type->get(), *rhs.return_type->get(), database)) {
+ return result;
+ }
+ }
+
+ if(lhs.parameters.has_value() && rhs.parameters.has_value()) {
+ if(lhs.parameters->size() != rhs.parameters->size()) {
+ return CompareFailReason::FUNCTION_PARAMAETER_COUNT;
+ }
+ for(size_t i = 0; i < lhs.parameters->size(); i++) {
+ if(compare_nodes_and_merge(result, *(*lhs.parameters)[i].get(), *(*rhs.parameters)[i].get(), database)) {
+ return result;
+ }
+ }
+ } else if(lhs.parameters.has_value() != rhs.parameters.has_value()) {
+ return CompareFailReason::FUNCTION_PARAMETERS_HAS_VALUE;
+ }
+
+ if(lhs.modifier != rhs.modifier) {
+ return CompareFailReason::FUNCTION_MODIFIER;
+ }
+
+ break;
+ }
+ case POINTER_OR_REFERENCE: {
+ const auto [lhs, rhs] = Node::as(node_lhs, node_rhs);
+
+ if(lhs.is_pointer != rhs.is_pointer) {
+ return CompareFailReason::DESCRIPTOR;
+ }
+
+ if(compare_nodes_and_merge(result, *lhs.value_type.get(), *rhs.value_type.get(), database)) {
+ return result;
+ }
+
+ break;
+ }
+ case POINTER_TO_DATA_MEMBER: {
+ const auto [lhs, rhs] = Node::as(node_lhs, node_rhs);
+
+ if(compare_nodes_and_merge(result, *lhs.class_type.get(), *rhs.class_type.get(), database)) {
+ return result;
+ }
+
+ if(compare_nodes_and_merge(result, *lhs.member_type.get(), *rhs.member_type.get(), database)) {
+ return result;
+ }
+
+ break;
+ }
+ case STRUCT_OR_UNION: {
+ const auto [lhs, rhs] = Node::as(node_lhs, node_rhs);
+
+ if(lhs.is_struct != rhs.is_struct) {
+ return CompareFailReason::DESCRIPTOR;
+ }
+
+ if(lhs.base_classes.size() != rhs.base_classes.size()) {
+ return CompareFailReason::BASE_CLASS_COUNT;
+ }
+
+ for(size_t i = 0; i < lhs.base_classes.size(); i++) {
+ if(compare_nodes_and_merge(result, *lhs.base_classes[i].get(), *rhs.base_classes[i].get(), database)) {
+ return result;
+ }
+ }
+
+ if(lhs.fields.size() != rhs.fields.size()) {
+ return CompareFailReason::FIELDS_SIZE;
+ }
+
+ for(size_t i = 0; i < lhs.fields.size(); i++) {
+ if(compare_nodes_and_merge(result, *lhs.fields[i].get(), *rhs.fields[i].get(), database)) {
+ return result;
+ }
+ }
+
+ if(lhs.member_functions.size() != rhs.member_functions.size()) {
+ return CompareFailReason::MEMBER_FUNCTION_COUNT;
+ }
+
+ for(size_t i = 0; i < lhs.member_functions.size(); i++) {
+ if(compare_nodes_and_merge(result, *lhs.member_functions[i].get(), *rhs.member_functions[i].get(), database)) {
+ return result;
+ }
+ }
+
+ break;
+ }
+ case TYPE_NAME: {
+ const auto [lhs, rhs] = Node::as(node_lhs, node_rhs);
+
+ // Don't check the source so that REFERENCE and CROSS_REFERENCE are
+ // treated as the same.
+ if(lhs.data_type_handle != rhs.data_type_handle) {
+ return CompareFailReason::TYPE_NAME;
+ }
+
+ const TypeName::UnresolvedStabs* lhs_unresolved_stabs = lhs.unresolved_stabs.get();
+ const TypeName::UnresolvedStabs* rhs_unresolved_stabs = rhs.unresolved_stabs.get();
+ if(lhs_unresolved_stabs && rhs_unresolved_stabs) {
+ if(lhs_unresolved_stabs->type_name != rhs_unresolved_stabs->type_name) {
+ return CompareFailReason::TYPE_NAME;
+ }
+ } else if(lhs_unresolved_stabs || rhs_unresolved_stabs) {
+ return CompareFailReason::TYPE_NAME;
+ }
+
+ break;
+ }
+ }
+ return result;
+}
+
+static bool compare_nodes_and_merge(
+ CompareResult& dest, const Node& node_lhs, const Node& node_rhs, const SymbolDatabase* database)
+{
+ CompareResult result = compare_nodes(node_lhs, node_rhs, database, true);
+ if(database) {
+ if(result.type == CompareResultType::DIFFERS && try_to_match_wobbly_typedefs(node_lhs, node_rhs, *database)) {
+ result.type = CompareResultType::MATCHES_FAVOUR_LHS;
+ } else if(result.type == CompareResultType::DIFFERS && try_to_match_wobbly_typedefs(node_rhs, node_lhs, *database)) {
+ result.type = CompareResultType::MATCHES_FAVOUR_RHS;
+ }
+ }
+
+ if(dest.type != result.type) {
+ if(dest.type == CompareResultType::DIFFERS || result.type == CompareResultType::DIFFERS) {
+ // If any of the inner types differ, the outer type does too.
+ dest.type = CompareResultType::DIFFERS;
+ } else if(dest.type == CompareResultType::MATCHES_CONFUSED || result.type == CompareResultType::MATCHES_CONFUSED) {
+ // Propagate confusion.
+ dest.type = CompareResultType::MATCHES_CONFUSED;
+ } else if(dest.type == CompareResultType::MATCHES_FAVOUR_LHS && result.type == CompareResultType::MATCHES_FAVOUR_RHS) {
+ // One of the results favours the LHS node and the other favours the
+ // RHS node so we are confused.
+ dest.type = CompareResultType::MATCHES_CONFUSED;
+ } else if(dest.type == CompareResultType::MATCHES_FAVOUR_RHS && result.type == CompareResultType::MATCHES_FAVOUR_LHS) {
+ // One of the results favours the LHS node and the other favours the
+ // RHS node so we are confused.
+ dest.type = CompareResultType::MATCHES_CONFUSED;
+ } else if(dest.type == CompareResultType::MATCHES_FAVOUR_LHS || result.type == CompareResultType::MATCHES_FAVOUR_LHS) {
+ // One of the results favours the LHS node and the other is neutral
+ // so go with the LHS node.
+ dest.type = CompareResultType::MATCHES_FAVOUR_LHS;
+ } else if(dest.type == CompareResultType::MATCHES_FAVOUR_RHS || result.type == CompareResultType::MATCHES_FAVOUR_RHS) {
+ // One of the results favours the RHS node and the other is neutral
+ // so go with the RHS node.
+ dest.type = CompareResultType::MATCHES_FAVOUR_RHS;
+ }
+ }
+
+ if(dest.fail_reason == CompareFailReason::NONE) {
+ dest.fail_reason = result.fail_reason;
+ }
+
+ return dest.type == CompareResultType::DIFFERS;
+}
+
+static bool try_to_match_wobbly_typedefs(
+ const Node& type_name_node, const Node& raw_node, const SymbolDatabase& database)
+{
+ // Detect if one side has a typedef when the other just has the plain type.
+ // This was previously a common reason why type deduplication would fail.
+ if(type_name_node.descriptor != TYPE_NAME) {
+ return false;
+ }
+
+ const TypeName& type_name = type_name_node.as();
+ if(const TypeName::UnresolvedStabs* unresolved_stabs = type_name.unresolved_stabs.get()) {
+ if(unresolved_stabs->referenced_file_handle == (u32) -1 || !unresolved_stabs->stabs_type_number.valid()) {
+ return false;
+ }
+
+ const SourceFile* source_file =
+ database.source_files.symbol_from_handle(unresolved_stabs->referenced_file_handle);
+ CCC_ASSERT(source_file);
+
+ auto handle = source_file->stabs_type_number_to_handle.find(unresolved_stabs->stabs_type_number);
+ if(handle != source_file->stabs_type_number_to_handle.end()) {
+ const DataType* referenced_type = database.data_types.symbol_from_handle(handle->second);
+ CCC_ASSERT(referenced_type && referenced_type->type());
+ // Don't compare 'intrusive' fields e.g. the offset.
+ CompareResult new_result = compare_nodes(*referenced_type->type(), raw_node, &database, false);
+ if(new_result.type != CompareResultType::DIFFERS) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+const char* compare_fail_reason_to_string(CompareFailReason reason)
+{
+ switch(reason) {
+ case CompareFailReason::NONE: return "error";
+ case CompareFailReason::DESCRIPTOR: return "descriptor";
+ case CompareFailReason::STORAGE_CLASS: return "storage class";
+ case CompareFailReason::NAME: return "name";
+ case CompareFailReason::RELATIVE_OFFSET_BYTES: return "relative offset";
+ case CompareFailReason::ABSOLUTE_OFFSET_BYTES: return "absolute offset";
+ case CompareFailReason::BITFIELD_OFFSET_BITS: return "bitfield offset";
+ case CompareFailReason::SIZE_BITS: return "size";
+ case CompareFailReason::CONSTNESS: return "constness";
+ case CompareFailReason::ARRAY_ELEMENT_COUNT: return "array element count";
+ case CompareFailReason::BUILTIN_CLASS: return "builtin class";
+ case CompareFailReason::FUNCTION_RETURN_TYPE_HAS_VALUE: return "function return type has value";
+ case CompareFailReason::FUNCTION_PARAMAETER_COUNT: return "function paramaeter count";
+ case CompareFailReason::FUNCTION_PARAMETERS_HAS_VALUE: return "function parameter";
+ case CompareFailReason::FUNCTION_MODIFIER: return "function modifier";
+ case CompareFailReason::ENUM_CONSTANTS: return "enum constant";
+ case CompareFailReason::BASE_CLASS_COUNT: return "base class count";
+ case CompareFailReason::FIELDS_SIZE: return "fields size";
+ case CompareFailReason::MEMBER_FUNCTION_COUNT: return "member function count";
+ case CompareFailReason::VTABLE_GLOBAL: return "vtable global";
+ case CompareFailReason::TYPE_NAME: return "type name";
+ case CompareFailReason::VARIABLE_CLASS: return "variable class";
+ case CompareFailReason::VARIABLE_TYPE: return "variable type";
+ case CompareFailReason::VARIABLE_STORAGE: return "variable storage";
+ case CompareFailReason::VARIABLE_BLOCK: return "variable block";
+ }
+ return "";
+}
+
+const char* node_type_to_string(const Node& node)
+{
+ switch(node.descriptor) {
+ case ARRAY: return "array";
+ case BITFIELD: return "bitfield";
+ case BUILTIN: return "builtin";
+ case ENUM: return "enum";
+ case ERROR_NODE: return "error";
+ case FUNCTION: return "function";
+ case POINTER_OR_REFERENCE: {
+ const PointerOrReference& pointer_or_reference = node.as();
+ if(pointer_or_reference.is_pointer) {
+ return "pointer";
+ } else {
+ return "reference";
+ }
+ }
+ case POINTER_TO_DATA_MEMBER: return "pointer_to_data_member";
+ case STRUCT_OR_UNION: {
+ const StructOrUnion& struct_or_union = node.as();
+ if(struct_or_union.is_struct) {
+ return "struct";
+ } else {
+ return "union";
+ }
+ }
+ case TYPE_NAME: return "type_name";
+ }
+ return "";
+}
+
+const char* storage_class_to_string(StorageClass storage_class)
+{
+ switch(storage_class) {
+ case STORAGE_CLASS_NONE: return "none";
+ case STORAGE_CLASS_TYPEDEF: return "typedef";
+ case STORAGE_CLASS_EXTERN: return "extern";
+ case STORAGE_CLASS_STATIC: return "static";
+ case STORAGE_CLASS_AUTO: return "auto";
+ case STORAGE_CLASS_REGISTER: return "register";
+ }
+ return "";
+}
+
+const char* access_specifier_to_string(AccessSpecifier specifier)
+{
+ switch(specifier) {
+ case AS_PUBLIC: return "public";
+ case AS_PROTECTED: return "protected";
+ case AS_PRIVATE: return "private";
+ }
+ return "";
+}
+
+const char* builtin_class_to_string(BuiltInClass bclass)
+{
+ switch(bclass) {
+ case BuiltInClass::VOID_TYPE: return "void";
+ case BuiltInClass::UNSIGNED_8: return "8-bit unsigned integer";
+ case BuiltInClass::SIGNED_8: return "8-bit signed integer";
+ case BuiltInClass::UNQUALIFIED_8: return "8-bit integer";
+ case BuiltInClass::BOOL_8: return "8-bit boolean";
+ case BuiltInClass::UNSIGNED_16: return "16-bit unsigned integer";
+ case BuiltInClass::SIGNED_16: return "16-bit signed integer";
+ case BuiltInClass::UNSIGNED_32: return "32-bit unsigned integer";
+ case BuiltInClass::SIGNED_32: return "32-bit signed integer";
+ case BuiltInClass::FLOAT_32: return "32-bit floating point";
+ case BuiltInClass::UNSIGNED_64: return "64-bit unsigned integer";
+ case BuiltInClass::SIGNED_64: return "64-bit signed integer";
+ case BuiltInClass::FLOAT_64: return "64-bit floating point";
+ case BuiltInClass::UNSIGNED_128: return "128-bit unsigned integer";
+ case BuiltInClass::SIGNED_128: return "128-bit signed integer";
+ case BuiltInClass::UNQUALIFIED_128: return "128-bit integer";
+ case BuiltInClass::FLOAT_128: return "128-bit floating point";
+ }
+ return "";
+}
+
+s32 builtin_class_size(BuiltInClass bclass)
+{
+ switch(bclass) {
+ case BuiltInClass::VOID_TYPE: return 0;
+ case BuiltInClass::UNSIGNED_8: return 1;
+ case BuiltInClass::SIGNED_8: return 1;
+ case BuiltInClass::UNQUALIFIED_8: return 1;
+ case BuiltInClass::BOOL_8: return 1;
+ case BuiltInClass::UNSIGNED_16: return 2;
+ case BuiltInClass::SIGNED_16: return 2;
+ case BuiltInClass::UNSIGNED_32: return 4;
+ case BuiltInClass::SIGNED_32: return 4;
+ case BuiltInClass::FLOAT_32: return 4;
+ case BuiltInClass::UNSIGNED_64: return 8;
+ case BuiltInClass::SIGNED_64: return 8;
+ case BuiltInClass::FLOAT_64: return 8;
+ case BuiltInClass::UNSIGNED_128: return 16;
+ case BuiltInClass::SIGNED_128: return 16;
+ case BuiltInClass::UNQUALIFIED_128: return 16;
+ case BuiltInClass::FLOAT_128: return 16;
+ }
+ return 0;
+}
+
+}
diff --git a/3rdparty/ccc/src/ccc/ast.h b/3rdparty/ccc/src/ccc/ast.h
new file mode 100644
index 0000000000..a34fa2e446
--- /dev/null
+++ b/3rdparty/ccc/src/ccc/ast.h
@@ -0,0 +1,377 @@
+// This file is part of the Chaos Compiler Collection.
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "symbol_database.h"
+
+namespace ccc::ast {
+
+enum NodeDescriptor : u8 {
+ ARRAY,
+ BITFIELD,
+ BUILTIN,
+ ENUM,
+ ERROR_NODE,
+ FUNCTION,
+ POINTER_OR_REFERENCE,
+ POINTER_TO_DATA_MEMBER,
+ STRUCT_OR_UNION,
+ TYPE_NAME
+};
+
+enum AccessSpecifier {
+ AS_PUBLIC = 0,
+ AS_PROTECTED = 1,
+ AS_PRIVATE = 2
+};
+
+// To add a new type of node:
+// 1. Add it to the NodeDescriptor enum.
+// 2. Create a struct for it.
+// 3. Add support for it in for_each_node.
+// 4. Add support for it in compute_size_bytes_recursive.
+// 5. Add support for it in compare_nodes.
+// 6. Add support for it in node_type_to_string.
+// 7. Add support for it in CppPrinter::ast_node.
+// 8. Add support for it in write_json.
+// 9. Add support for it in refine_node.
+struct Node {
+ const NodeDescriptor descriptor;
+ u8 is_const : 1 = false;
+ u8 is_volatile : 1 = false;
+ u8 is_virtual_base_class : 1 = false;
+ u8 is_vtable_pointer : 1 = false;
+ u8 is_constructor_or_destructor : 1 = false;
+ u8 is_special_member_function : 1 = false;
+ u8 is_operator_member_function : 1 = false;
+ u8 cannot_compute_size : 1 = false;
+ u8 storage_class : 4 = STORAGE_CLASS_NONE;
+ u8 access_specifier : 2 = AS_PUBLIC;
+
+ s32 size_bytes = -1;
+
+ // If the name isn't populated for a given node, the name from the last
+ // ancestor to have one should be used i.e. when processing the tree you
+ // should pass the name down.
+ std::string name;
+
+ s32 offset_bytes = -1; // Offset relative to start of last inline struct/union.
+ s32 size_bits = -1; // Size stored in the .mdebug symbol table, may not be set.
+
+ Node(NodeDescriptor d) : descriptor(d) {}
+ Node(const Node& rhs) = default;
+ virtual ~Node() {}
+
+ template
+ SubType& as() {
+ CCC_ASSERT(descriptor == SubType::DESCRIPTOR);
+ return *static_cast(this);
+ }
+
+ template
+ const SubType& as() const {
+ CCC_ASSERT(descriptor == SubType::DESCRIPTOR);
+ return *static_cast(this);
+ }
+
+ template
+ static std::pair as(const Node& lhs, const Node& rhs) {
+ CCC_ASSERT(lhs.descriptor == SubType::DESCRIPTOR && rhs.descriptor == SubType::DESCRIPTOR);
+ return std::pair(static_cast(lhs), static_cast(rhs));
+ }
+
+ void set_access_specifier(AccessSpecifier specifier, u32 importer_flags);
+
+ // If this node is a type name, repeatedly resolve it to the type it's
+ // referencing, otherwise return (this, nullptr).
+ std::pair physical_type(SymbolDatabase& database, s32 max_depth = 100);
+ std::pair physical_type(const SymbolDatabase& database, s32 max_depth = 100) const;
+};
+
+struct Array : Node {
+ std::unique_ptr element_type;
+ s32 element_count = -1;
+
+ Array() : Node(DESCRIPTOR) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = ARRAY;
+};
+
+struct BitField : Node {
+ s32 bitfield_offset_bits = -1; // Offset relative to the last byte (not the position of the underlying type!).
+ std::unique_ptr underlying_type;
+
+ BitField() : Node(DESCRIPTOR) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = BITFIELD;
+};
+
+enum class BuiltInClass {
+ VOID_TYPE,
+ UNSIGNED_8, SIGNED_8, UNQUALIFIED_8, BOOL_8,
+ UNSIGNED_16, SIGNED_16,
+ UNSIGNED_32, SIGNED_32, FLOAT_32,
+ UNSIGNED_64, SIGNED_64, FLOAT_64,
+ UNSIGNED_128, SIGNED_128, UNQUALIFIED_128, FLOAT_128
+};
+
+struct BuiltIn : Node {
+ BuiltInClass bclass = BuiltInClass::VOID_TYPE;
+
+ BuiltIn() : Node(DESCRIPTOR) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = BUILTIN;
+};
+
+struct Enum : Node {
+ std::vector> constants;
+
+ Enum() : Node(DESCRIPTOR) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = ENUM;
+};
+
+struct Error : Node {
+ std::string message;
+
+ Error() : Node(ERROR_NODE) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = ERROR_NODE;
+};
+
+enum class MemberFunctionModifier {
+ NONE,
+ STATIC,
+ VIRTUAL
+};
+
+const char* member_function_modifier_to_string(MemberFunctionModifier modifier);
+
+struct Function : Node {
+ std::optional> return_type;
+ std::optional>> parameters;
+ MemberFunctionModifier modifier = MemberFunctionModifier::NONE;
+ s32 vtable_index = -1;
+ FunctionHandle definition_handle; // Filled in by fill_in_pointers_to_member_function_definitions.
+
+ Function() : Node(DESCRIPTOR) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = FUNCTION;
+};
+
+struct PointerOrReference : Node {
+ bool is_pointer = true;
+ std::unique_ptr value_type;
+
+ PointerOrReference() : Node(DESCRIPTOR) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = POINTER_OR_REFERENCE;
+};
+
+struct PointerToDataMember : Node {
+ std::unique_ptr class_type;
+ std::unique_ptr member_type;
+
+ PointerToDataMember() : Node(DESCRIPTOR) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = POINTER_TO_DATA_MEMBER;
+};
+
+struct StructOrUnion : Node {
+ bool is_struct = true;
+ std::vector> base_classes;
+ std::vector> fields;
+ std::vector> member_functions;
+
+ StructOrUnion() : Node(DESCRIPTOR) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = STRUCT_OR_UNION;
+
+ struct FlatField {
+ // The field itself.
+ const Node* node;
+ // The symbol that owns the node.
+ const DataType* symbol;
+ // Offset of the innermost enclosing base class in the object.
+ s32 base_offset = 0;
+ };
+
+ // Generate a flat list of all the fields in this class as well as all the
+ // base classes recursively, but only until the max_fields or max_depth
+ // limits are reached. Return true if all the fields were enumerated.
+ bool flatten_fields(
+ std::vector& output,
+ const DataType* symbol,
+ const SymbolDatabase& database,
+ bool skip_statics,
+ s32 base_offset = 0,
+ s32 max_fields = 100000,
+ s32 max_depth = 100) const;
+};
+
+enum class TypeNameSource : u8 {
+ REFERENCE, // A STABS type reference.
+ CROSS_REFERENCE, // A STABS cross reference.
+ UNNAMED_THIS // A this parameter (or return type) referencing an unnamed type.
+};
+
+const char* type_name_source_to_string(TypeNameSource source);
+
+enum class ForwardDeclaredType {
+ STRUCT,
+ UNION,
+ ENUM // Should be illegal but STABS supports cross references to enums so it's here.
+};
+
+const char* forward_declared_type_to_string(ForwardDeclaredType type);
+
+struct TypeName : Node {
+ DataTypeHandle data_type_handle;
+ TypeNameSource source = TypeNameSource::REFERENCE;
+ bool is_forward_declared = false;
+
+ DataTypeHandle data_type_handle_unless_forward_declared() const;
+
+ struct UnresolvedStabs {
+ std::string type_name;
+ SourceFileHandle referenced_file_handle;
+ StabsTypeNumber stabs_type_number;
+ std::optional type;
+ };
+
+ std::unique_ptr unresolved_stabs;
+
+ TypeName() : Node(DESCRIPTOR) {}
+ static const constexpr NodeDescriptor DESCRIPTOR = TYPE_NAME;
+};
+
+enum class CompareResultType {
+ MATCHES_NO_SWAP, // Both lhs and rhs are identical.
+ MATCHES_CONFUSED, // Both lhs and rhs are almost identical, and we don't which is better.
+ MATCHES_FAVOUR_LHS, // Both lhs and rhs are almost identical, but lhs is better.
+ MATCHES_FAVOUR_RHS, // Both lhs and rhs are almost identical, but rhs is better.
+ DIFFERS, // The two nodes differ substantially.
+};
+
+enum class CompareFailReason {
+ NONE,
+ DESCRIPTOR,
+ STORAGE_CLASS,
+ NAME,
+ RELATIVE_OFFSET_BYTES,
+ ABSOLUTE_OFFSET_BYTES,
+ BITFIELD_OFFSET_BITS,
+ SIZE_BITS,
+ CONSTNESS,
+ ARRAY_ELEMENT_COUNT,
+ BUILTIN_CLASS,
+ FUNCTION_RETURN_TYPE_HAS_VALUE,
+ FUNCTION_PARAMAETER_COUNT,
+ FUNCTION_PARAMETERS_HAS_VALUE,
+ FUNCTION_MODIFIER,
+ ENUM_CONSTANTS,
+ BASE_CLASS_COUNT,
+ FIELDS_SIZE,
+ MEMBER_FUNCTION_COUNT,
+ VTABLE_GLOBAL,
+ TYPE_NAME,
+ VARIABLE_CLASS,
+ VARIABLE_TYPE,
+ VARIABLE_STORAGE,
+ VARIABLE_BLOCK
+};
+
+struct CompareResult {
+ CompareResult(CompareResultType type) : type(type), fail_reason(CompareFailReason::NONE) {}
+ CompareResult(CompareFailReason reason) : type(CompareResultType::DIFFERS), fail_reason(reason) {}
+ CompareResultType type;
+ CompareFailReason fail_reason;
+};
+
+// Compare two AST nodes and their children recursively. This will only check
+// fields that will be equal for two versions of the same type from different
+// translation units.
+CompareResult compare_nodes(const Node& lhs, const Node& rhs, const SymbolDatabase* database, bool check_intrusive_fields);
+
+const char* compare_fail_reason_to_string(CompareFailReason reason);
+const char* node_type_to_string(const Node& node);
+const char* storage_class_to_string(StorageClass storage_class);
+const char* access_specifier_to_string(AccessSpecifier specifier);
+const char* builtin_class_to_string(BuiltInClass bclass);
+
+s32 builtin_class_size(BuiltInClass bclass);
+
+enum TraversalOrder {
+ PREORDER_TRAVERSAL,
+ POSTORDER_TRAVERSAL
+};
+
+enum ExplorationMode {
+ EXPLORE_CHILDREN,
+ DONT_EXPLORE_CHILDREN
+};
+
+template
+void for_each_node(ThisNode& node, TraversalOrder order, Callback callback)
+{
+ if(order == PREORDER_TRAVERSAL && callback(node) == DONT_EXPLORE_CHILDREN) {
+ return;
+ }
+ switch(node.descriptor) {
+ case ARRAY: {
+ auto& array = node.template as();
+ for_each_node(*array.element_type.get(), order, callback);
+ break;
+ }
+ case BITFIELD: {
+ auto& bitfield = node.template as();
+ for_each_node(*bitfield.underlying_type.get(), order, callback);
+ break;
+ }
+ case BUILTIN: {
+ break;
+ }
+ case ENUM: {
+ break;
+ }
+ case ERROR_NODE: {
+ break;
+ }
+ case FUNCTION: {
+ auto& func = node.template as();
+ if(func.return_type.has_value()) {
+ for_each_node(*func.return_type->get(), order, callback);
+ }
+ if(func.parameters.has_value()) {
+ for(auto& child : *func.parameters) {
+ for_each_node(*child.get(), order, callback);
+ }
+ }
+ break;
+ }
+ case POINTER_OR_REFERENCE: {
+ auto& pointer_or_reference = node.template as();
+ for_each_node(*pointer_or_reference.value_type.get(), order, callback);
+ break;
+ }
+ case POINTER_TO_DATA_MEMBER: {
+ auto& pointer = node.template as();
+ for_each_node(*pointer.class_type.get(), order, callback);
+ for_each_node(*pointer.member_type.get(), order, callback);
+ break;
+ }
+ case STRUCT_OR_UNION: {
+ auto& struct_or_union = node.template as();
+ for(auto& child : struct_or_union.base_classes) {
+ for_each_node(*child.get(), order, callback);
+ }
+ for(auto& child : struct_or_union.fields) {
+ for_each_node(*child.get(), order, callback);
+ }
+ for(auto& child : struct_or_union.member_functions) {
+ for_each_node(*child.get(), order, callback);
+ }
+ break;
+ }
+ case TYPE_NAME: {
+ break;
+ }
+ }
+ if(order == POSTORDER_TRAVERSAL) {
+ callback(node);
+ }
+}
+
+}
diff --git a/3rdparty/ccc/src/ccc/elf.cpp b/3rdparty/ccc/src/ccc/elf.cpp
new file mode 100644
index 0000000000..8c3ed99e57
--- /dev/null
+++ b/3rdparty/ccc/src/ccc/elf.cpp
@@ -0,0 +1,125 @@
+// This file is part of the Chaos Compiler Collection.
+// SPDX-License-Identifier: MIT
+
+#include "elf.h"
+
+namespace ccc {
+
+Result ElfFile::parse(std::vector image)
+{
+ ElfFile elf;
+ elf.image = std::move(image);
+
+ const ElfIdentHeader* ident = get_packed(elf.image, 0);
+ CCC_CHECK(ident, "ELF ident header out of range.");
+ CCC_CHECK(ident->magic == CCC_FOURCC("\x7f\x45\x4c\x46"), "Not an ELF file.");
+ CCC_CHECK(ident->e_class == ElfIdentClass::B32, "Wrong ELF class (not 32 bit).");
+
+ const ElfFileHeader* header = get_packed(elf.image, sizeof(ElfIdentHeader));
+ CCC_CHECK(header, "ELF file header out of range.");
+ elf.file_header = *header;
+
+ const ElfSectionHeader* shstr_section_header = get_packed(elf.image, header->shoff + header->shstrndx * sizeof(ElfSectionHeader));
+ CCC_CHECK(shstr_section_header, "ELF section name header out of range.");
+
+ for(u32 i = 0; i < header->shnum; i++) {
+ u64 header_offset = header->shoff + i * sizeof(ElfSectionHeader);
+ const ElfSectionHeader* section_header = get_packed(elf.image, header_offset);
+ CCC_CHECK(section_header, "ELF section header out of range.");
+
+ const char* name = get_string(elf.image, shstr_section_header->offset + section_header->name);
+ CCC_CHECK(section_header, "ELF section name out of range.");
+
+ ElfSection& section = elf.sections.emplace_back();
+ section.name = name;
+ section.header = *section_header;
+ }
+
+ for(u32 i = 0; i < header->phnum; i++) {
+ u64 header_offset = header->phoff + i * sizeof(ElfProgramHeader);
+ const ElfProgramHeader* program_header = get_packed(elf.image, header_offset);
+ CCC_CHECK(program_header, "ELF program header out of range.");
+
+ elf.segments.emplace_back(*program_header);
+ }
+
+ return elf;
+}
+
+Result ElfFile::create_section_symbols(
+ SymbolDatabase& database, const SymbolGroup& group) const
+{
+ for(const ElfSection& section : sections) {
+ Address address = Address::non_zero(section.header.addr);
+
+ Result symbol = database.sections.create_symbol(
+ section.name, address, group.source, group.module_symbol);
+ CCC_RETURN_IF_ERROR(symbol);
+
+ (*symbol)->set_size(section.header.size);
+ }
+
+ return Result();
+}
+
+const ElfSection* ElfFile::lookup_section(const char* name) const
+{
+ for(const ElfSection& section : sections) {
+ if(section.name == name) {
+ return §ion;
+ }
+ }
+ return nullptr;
+}
+
+std::optional ElfFile::file_offset_to_virtual_address(u32 file_offset) const
+{
+ for(const ElfProgramHeader& segment : segments) {
+ if(file_offset >= segment.offset && file_offset < segment.offset + segment.filesz) {
+ return segment.vaddr + file_offset - segment.offset;
+ }
+ }
+ return std::nullopt;
+}
+
+const ElfProgramHeader* ElfFile::entry_point_segment() const
+{
+ const ccc::ElfProgramHeader* entry_segment = nullptr;
+ for(const ccc::ElfProgramHeader& segment : segments) {
+ if(file_header.entry >= segment.vaddr && file_header.entry < segment.vaddr + segment.filesz) {
+ entry_segment = &segment;
+ }
+ }
+ return entry_segment;
+}
+
+Result> ElfFile::get_virtual(u32 address, u32 size) const
+{
+ u32 end_address = address + size;
+
+ if(end_address >= address) {
+ for(const ElfProgramHeader& segment : segments) {
+ if(address >= segment.vaddr && end_address <= segment.vaddr + segment.filesz) {
+ size_t begin_offset = segment.offset + (address - segment.vaddr);
+ size_t end_offset = begin_offset + size;
+ if(begin_offset <= image.size() && end_offset <= image.size()) {
+ return std::span(image.data() + begin_offset, image.data() + end_offset);
+ }
+ }
+ }
+ }
+
+ return CCC_FAILURE("No ELF segment for address range 0x%x to 0x%x.", address, end_address);
+}
+
+Result ElfFile::copy_virtual(u8* dest, u32 address, u32 size) const
+{
+ Result> block = get_virtual(address, size);
+ CCC_RETURN_IF_ERROR(block);
+
+ memcpy(dest, block->data(), size);
+
+ return Result();
+}
+
+}
diff --git a/3rdparty/ccc/src/ccc/elf.h b/3rdparty/ccc/src/ccc/elf.h
new file mode 100644
index 0000000000..33d89e759c
--- /dev/null
+++ b/3rdparty/ccc/src/ccc/elf.h
@@ -0,0 +1,156 @@
+// This file is part of the Chaos Compiler Collection.
+// SPDX-License-Identifier: MIT
+
+#pragma once
+
+#include "symbol_database.h"
+
+namespace ccc {
+
+enum class ElfIdentClass : u8 {
+ B32 = 0x1,
+ B64 = 0x2
+};
+
+CCC_PACKED_STRUCT(ElfIdentHeader,
+ /* 0x0 */ u32 magic; // 7f 45 4c 46
+ /* 0x4 */ ElfIdentClass e_class;
+ /* 0x5 */ u8 endianess;
+ /* 0x6 */ u8 version;
+ /* 0x7 */ u8 os_abi;
+ /* 0x8 */ u8 abi_version;
+ /* 0x9 */ u8 pad[7];
+)
+
+enum class ElfFileType : u16 {
+ NONE = 0x00,
+ REL = 0x01,
+ EXEC = 0x02,
+ DYN = 0x03,
+ CORE = 0x04,
+ LOOS = 0xfe00,
+ HIOS = 0xfeff,
+ LOPROC = 0xff00,
+ HIPROC = 0xffff
+};
+
+enum class ElfMachine : u16 {
+ MIPS = 0x08
+};
+
+CCC_PACKED_STRUCT(ElfFileHeader,
+ /* 0x10 */ ElfFileType type;
+ /* 0x12 */ ElfMachine machine;
+ /* 0x14 */ u32 version;
+ /* 0x18 */ u32 entry;
+ /* 0x1c */ u32 phoff;
+ /* 0x20 */ u32 shoff;
+ /* 0x24 */ u32 flags;
+ /* 0x28 */ u16 ehsize;
+ /* 0x2a */ u16 phentsize;
+ /* 0x2c */ u16 phnum;
+ /* 0x2e */ u16 shentsize;
+ /* 0x30 */ u16 shnum;
+ /* 0x32 */ u16 shstrndx;
+)
+
+enum class ElfSectionType : u32 {
+ NULL_SECTION = 0x0,
+ PROGBITS = 0x1,
+ SYMTAB = 0x2,
+ STRTAB = 0x3,
+ RELA = 0x4,
+ HASH = 0x5,
+ DYNAMIC = 0x6,
+ NOTE = 0x7,
+ NOBITS = 0x8,
+ REL = 0x9,
+ SHLIB = 0xa,
+ DYNSYM = 0xb,
+ INIT_ARRAY = 0xe,
+ FINI_ARRAY = 0xf,
+ PREINIT_ARRAY = 0x10,
+ GROUP = 0x11,
+ SYMTAB_SHNDX = 0x12,
+ NUM = 0x13,
+ LOOS = 0x60000000,
+ MIPS_DEBUG = 0x70000005
+};
+
+CCC_PACKED_STRUCT(ElfSectionHeader,
+ /* 0x00 */ u32 name;
+ /* 0x04 */ ElfSectionType type;
+ /* 0x08 */ u32 flags;
+ /* 0x0c */ u32 addr;
+ /* 0x10 */ u32 offset;
+ /* 0x14 */ u32 size;
+ /* 0x18 */ u32 link;
+ /* 0x1c */ u32 info;
+ /* 0x20 */ u32 addralign;
+ /* 0x24 */ u32 entsize;
+)
+
+struct ElfSection {
+ std::string name;
+ ElfSectionHeader header;
+};
+
+CCC_PACKED_STRUCT(ElfProgramHeader,
+ /* 0x00 */ u32 type;
+ /* 0x04 */ u32 offset;
+ /* 0x08 */ u32 vaddr;
+ /* 0x0c */ u32 paddr;
+ /* 0x10 */ u32 filesz;
+ /* 0x14 */ u32 memsz;
+ /* 0x18 */ u32 flags;
+ /* 0x1c */ u32 align;
+)
+
+struct ElfFile {
+ ElfFileHeader file_header;
+ std::vector image;
+ std::vector sections;
+ std::vector segments;
+
+ // Parse the ELF file header, section headers and program headers.
+ static Result parse(std::vector image);
+
+ // Create a section object for each section header in the ELF file.
+ Result create_section_symbols(SymbolDatabase& database, const SymbolGroup& group) const;
+
+ const ElfSection* lookup_section(const char* name) const;
+ std::optional file_offset_to_virtual_address(u32 file_offset) const;
+
+ // Find the program header for the segment that contains the entry point.
+ const ElfProgramHeader* entry_point_segment() const;
+
+ // Retrieve a block of data in an ELF file given its address and size.
+ Result> get_virtual(u32 address, u32 size) const;
+
+ // Copy a block of data in an ELF file to the destination buffer given its
+ // address and size.
+ Result copy_virtual(u8* dest, u32 address, u32 size) const;
+
+ // Retrieve an object of type T from an ELF file given its address.
+ template
+ Result get_object_virtual(u32 address) const
+ {
+ Result> result = get_virtual(address, sizeof(T));
+ CCC_RETURN_IF_ERROR(result);
+
+ return *(T*) result->data();
+ }
+
+ // Retrieve an array of objects of type T from an ELF file given its
+ // address and element count.
+ template
+ Result> get_array_virtual(u32 address, u32 element_count) const
+ {
+ Result> result = get_virtual(address, element_count * sizeof(T));
+ CCC_RETURN_IF_ERROR(result);
+
+ return std::span((T*) result->data(), (T*) (result->data() + result->size()));
+ }
+};
+
+}
diff --git a/3rdparty/ccc/src/ccc/elf_symtab.cpp b/3rdparty/ccc/src/ccc/elf_symtab.cpp
new file mode 100644
index 0000000000..892ea5ec2d
--- /dev/null
+++ b/3rdparty/ccc/src/ccc/elf_symtab.cpp
@@ -0,0 +1,213 @@
+// This file is part of the Chaos Compiler Collection.
+// SPDX-License-Identifier: MIT
+
+#include "elf_symtab.h"
+
+#include "importer_flags.h"
+
+namespace ccc::elf {
+
+enum class SymbolBind : u8 {
+ LOCAL = 0,
+ GLOBAL = 1,
+ WEAK = 2,
+ NUM = 3,
+ GNU_UNIQUE = 10
+};
+
+enum class SymbolType : u8 {
+ NOTYPE = 0,
+ OBJECT = 1,
+ FUNC = 2,
+ SECTION = 3,
+ FILE = 4,
+ COMMON = 5,
+ TLS = 6,
+ NUM = 7,
+ GNU_IFUNC = 10
+};
+
+enum class SymbolVisibility {
+ DEFAULT = 0,
+ INTERNAL = 1,
+ HIDDEN = 2,
+ PROTECTED = 3
+};
+
+CCC_PACKED_STRUCT(Symbol,
+ /* 0x0 */ u32 name;
+ /* 0x4 */ u32 value;
+ /* 0x8 */ u32 size;
+ /* 0xc */ u8 info;
+ /* 0xd */ u8 other;
+ /* 0xe */ u16 shndx;
+
+ SymbolType type() const { return (SymbolType) (info & 0xf); }
+ SymbolBind bind() const { return (SymbolBind) (info >> 4); }
+ SymbolVisibility visibility() const { return (SymbolVisibility) (other & 0x3); }
+)
+
+static const char* symbol_bind_to_string(SymbolBind bind);
+static const char* symbol_type_to_string(SymbolType type);
+static const char* symbol_visibility_to_string(SymbolVisibility visibility);
+
+Result import_symbols(
+ SymbolDatabase& database,
+ const SymbolGroup& group,
+ std::span symtab,
+ std::span strtab,
+ u32 importer_flags,
+ DemanglerFunctions demangler)
+{
+ for(u32 i = 0; i < symtab.size() / sizeof(Symbol); i++) {
+ const Symbol* symbol = get_packed(symtab, i * sizeof(Symbol));
+ CCC_ASSERT(symbol);
+
+ Address address;
+ if(symbol->value != 0) {
+ address = symbol->value;
+ }
+
+ if(!address.valid() || symbol->visibility() != SymbolVisibility::DEFAULT) {
+ continue;
+ }
+
+ if(!(importer_flags & DONT_DEDUPLICATE_SYMBOLS)) {
+ if(database.functions.first_handle_from_starting_address(address).valid()) {
+ continue;
+ }
+
+ if(database.global_variables.first_handle_from_starting_address(address).valid()) {
+ continue;
+ }
+
+ if(database.local_variables.first_handle_from_starting_address(address).valid()) {
+ continue;
+ }
+ }
+
+ const char* string = get_string(strtab, symbol->name);
+ CCC_CHECK(string, "Symbol string out of range.");
+
+ switch(symbol->type()) {
+ case SymbolType::NOTYPE: {
+ Result