From 87b03fdc284c344d87e4959357835eb3938c5f3f Mon Sep 17 00:00:00 2001
From: chaoticgd <43898262+chaoticgd@users.noreply.github.com>
Date: Mon, 26 Aug 2024 18:08:33 +0100
Subject: [PATCH] 3rdparty: Add CCC v2.1
This is the symbol table parser that I'm replacing the existing ELF
symbol table parser with. It supports STABS symbols in .mdebug sections
as well as ELF symbols and SNDLL symbols.
It includes its own symbol database, and an AST which facilitates
debugging tools that let the user inspect complex data structures with
full type information.
More information is provided in the included readme.
---
3rdparty/ccc/CMakeLists.txt | 41 +
3rdparty/ccc/README.md | 37 +
3rdparty/ccc/ccc.vcxproj | 75 ++
3rdparty/ccc/ccc.vcxproj.filters | 111 ++
3rdparty/ccc/src/ccc/ast.cpp | 562 ++++++++++
3rdparty/ccc/src/ccc/ast.h | 377 +++++++
3rdparty/ccc/src/ccc/elf.cpp | 125 +++
3rdparty/ccc/src/ccc/elf.h | 156 +++
3rdparty/ccc/src/ccc/elf_symtab.cpp | 213 ++++
3rdparty/ccc/src/ccc/elf_symtab.h | 20 +
3rdparty/ccc/src/ccc/importer_flags.cpp | 95 ++
3rdparty/ccc/src/ccc/importer_flags.h | 39 +
3rdparty/ccc/src/ccc/mdebug_analysis.cpp | 349 +++++++
3rdparty/ccc/src/ccc/mdebug_analysis.h | 99 ++
3rdparty/ccc/src/ccc/mdebug_importer.cpp | 668 ++++++++++++
3rdparty/ccc/src/ccc/mdebug_importer.h | 31 +
3rdparty/ccc/src/ccc/mdebug_section.cpp | 474 +++++++++
3rdparty/ccc/src/ccc/mdebug_section.h | 176 ++++
3rdparty/ccc/src/ccc/mdebug_symbols.cpp | 220 ++++
3rdparty/ccc/src/ccc/mdebug_symbols.h | 32 +
3rdparty/ccc/src/ccc/sndll.cpp | 191 ++++
3rdparty/ccc/src/ccc/sndll.h | 55 +
3rdparty/ccc/src/ccc/stabs.cpp | 835 +++++++++++++++
3rdparty/ccc/src/ccc/stabs.h | 379 +++++++
3rdparty/ccc/src/ccc/stabs_to_ast.cpp | 834 +++++++++++++++
3rdparty/ccc/src/ccc/stabs_to_ast.h | 29 +
3rdparty/ccc/src/ccc/symbol_database.cpp | 1204 ++++++++++++++++++++++
3rdparty/ccc/src/ccc/symbol_database.h | 721 +++++++++++++
3rdparty/ccc/src/ccc/symbol_file.cpp | 114 ++
3rdparty/ccc/src/ccc/symbol_file.h | 62 ++
3rdparty/ccc/src/ccc/symbol_table.cpp | 283 +++++
3rdparty/ccc/src/ccc/symbol_table.h | 163 +++
3rdparty/ccc/src/ccc/util.cpp | 173 ++++
3rdparty/ccc/src/ccc/util.h | 312 ++++++
PCSX2_qt.sln | 42 +
cmake/SearchForStuff.cmake | 3 +
pcsx2-qt/pcsx2-qt.vcxproj | 1 +
pcsx2/CMakeLists.txt | 1 +
pcsx2/pcsx2.vcxproj | 4 +
39 files changed, 9306 insertions(+)
create mode 100644 3rdparty/ccc/CMakeLists.txt
create mode 100644 3rdparty/ccc/README.md
create mode 100644 3rdparty/ccc/ccc.vcxproj
create mode 100644 3rdparty/ccc/ccc.vcxproj.filters
create mode 100644 3rdparty/ccc/src/ccc/ast.cpp
create mode 100644 3rdparty/ccc/src/ccc/ast.h
create mode 100644 3rdparty/ccc/src/ccc/elf.cpp
create mode 100644 3rdparty/ccc/src/ccc/elf.h
create mode 100644 3rdparty/ccc/src/ccc/elf_symtab.cpp
create mode 100644 3rdparty/ccc/src/ccc/elf_symtab.h
create mode 100644 3rdparty/ccc/src/ccc/importer_flags.cpp
create mode 100644 3rdparty/ccc/src/ccc/importer_flags.h
create mode 100644 3rdparty/ccc/src/ccc/mdebug_analysis.cpp
create mode 100644 3rdparty/ccc/src/ccc/mdebug_analysis.h
create mode 100644 3rdparty/ccc/src/ccc/mdebug_importer.cpp
create mode 100644 3rdparty/ccc/src/ccc/mdebug_importer.h
create mode 100644 3rdparty/ccc/src/ccc/mdebug_section.cpp
create mode 100644 3rdparty/ccc/src/ccc/mdebug_section.h
create mode 100644 3rdparty/ccc/src/ccc/mdebug_symbols.cpp
create mode 100644 3rdparty/ccc/src/ccc/mdebug_symbols.h
create mode 100644 3rdparty/ccc/src/ccc/sndll.cpp
create mode 100644 3rdparty/ccc/src/ccc/sndll.h
create mode 100644 3rdparty/ccc/src/ccc/stabs.cpp
create mode 100644 3rdparty/ccc/src/ccc/stabs.h
create mode 100644 3rdparty/ccc/src/ccc/stabs_to_ast.cpp
create mode 100644 3rdparty/ccc/src/ccc/stabs_to_ast.h
create mode 100644 3rdparty/ccc/src/ccc/symbol_database.cpp
create mode 100644 3rdparty/ccc/src/ccc/symbol_database.h
create mode 100644 3rdparty/ccc/src/ccc/symbol_file.cpp
create mode 100644 3rdparty/ccc/src/ccc/symbol_file.h
create mode 100644 3rdparty/ccc/src/ccc/symbol_table.cpp
create mode 100644 3rdparty/ccc/src/ccc/symbol_table.h
create mode 100644 3rdparty/ccc/src/ccc/util.cpp
create mode 100644 3rdparty/ccc/src/ccc/util.h
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