mirror of
https://github.com/open-goal/jak-project
synced 2026-07-10 23:22:17 -04:00
Partially Implement Decompiler Type Analysis (#90)
* implement basic framework for decompiler type analysis * before type system changes * add some decompiler stuff to the type system * try algorithm on a few functions
This commit is contained in:
@@ -2,11 +2,13 @@
|
||||
* @file LinkedObjectFile.cpp
|
||||
* An object file's data with linking information included.
|
||||
*/
|
||||
#include "LinkedObjectFile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <numeric>
|
||||
#include "third-party/fmt/format.h"
|
||||
#include "LinkedObjectFile.h"
|
||||
#include "decompiler/Disasm/InstructionDecode.h"
|
||||
#include "decompiler/config.h"
|
||||
|
||||
@@ -556,9 +558,10 @@ std::string LinkedObjectFile::print_disassembly() {
|
||||
auto& word = words_by_seg[seg].at(func.start_word + i);
|
||||
append_word_to_string(result, word);
|
||||
} else {
|
||||
// print basic op stuff
|
||||
if (func.has_basic_ops() && func.instr_starts_basic_op(i)) {
|
||||
if (line.length() < 40) {
|
||||
line.append(40 - line.length(), ' ');
|
||||
if (line.length() < 30) {
|
||||
line.append(30 - line.length(), ' ');
|
||||
}
|
||||
line += ";; " + func.get_basic_op_at_instr(i)->print(*this);
|
||||
for (int iidx = 0; iidx < instr.n_src; iidx++) {
|
||||
@@ -569,6 +572,31 @@ std::string LinkedObjectFile::print_disassembly() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// print type map
|
||||
if (func.has_typemaps()) {
|
||||
if (line.length() < 60) {
|
||||
line.append(60 - line.length(), ' ');
|
||||
}
|
||||
line += " tm: ";
|
||||
auto& tm = func.get_typemap_by_instr_idx(i);
|
||||
bool added = false;
|
||||
for (auto reg_kind : {Reg::RegisterKind::GPR, Reg::RegisterKind::FPR}) {
|
||||
for (int reg_idx = 0; reg_idx < 32; reg_idx++) {
|
||||
auto gpr = Register(reg_kind, reg_idx);
|
||||
auto kv = tm.find(gpr);
|
||||
if (kv != tm.end()) {
|
||||
added = true;
|
||||
line += fmt::format("{}: {}, ", gpr.to_charp(), kv->second.print());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (added) {
|
||||
line.pop_back();
|
||||
line.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
result += line + "\n";
|
||||
}
|
||||
|
||||
@@ -602,6 +602,7 @@ void ObjectFileDB::analyze_functions() {
|
||||
int asm_funcs = 0;
|
||||
int non_asm_funcs = 0;
|
||||
int successful_cfg_irs = 0;
|
||||
int successful_type_analysis = 0;
|
||||
|
||||
std::map<int, std::vector<std::string>> unresolved_by_length;
|
||||
if (get_config().find_basic_blocks) {
|
||||
@@ -615,8 +616,15 @@ void ObjectFileDB::analyze_functions() {
|
||||
|
||||
total_functions++;
|
||||
if (!func.suspected_asm) {
|
||||
// run analysis
|
||||
|
||||
// first, find the prologue/epilogue
|
||||
func.analyze_prologue(data.linked_data);
|
||||
|
||||
// build a control flow graph
|
||||
func.cfg = build_cfg(data.linked_data, segment_id, func);
|
||||
|
||||
// convert individual basic blocks to sequences of IR Basic Ops
|
||||
for (auto& block : func.basic_blocks) {
|
||||
if (block.end_word > block.start_word) {
|
||||
add_basic_ops_to_block(&func, block, &data.linked_data);
|
||||
@@ -625,6 +633,7 @@ void ObjectFileDB::analyze_functions() {
|
||||
total_basic_ops += func.get_basic_op_count();
|
||||
total_failed_basic_ops += func.get_failed_basic_op_count();
|
||||
|
||||
// Combine basic ops + CFG to build a nested IR
|
||||
func.ir = build_cfg_ir(func, *func.cfg, data.linked_data);
|
||||
non_asm_funcs++;
|
||||
if (func.ir) {
|
||||
@@ -634,6 +643,26 @@ void ObjectFileDB::analyze_functions() {
|
||||
if (func.cfg->is_fully_resolved()) {
|
||||
resolved_cfg_functions++;
|
||||
}
|
||||
|
||||
// type analysis
|
||||
if (func.guessed_name.kind == FunctionName::FunctionKind::GLOBAL) {
|
||||
// we're a global named function. This means we're stored in a symbol
|
||||
auto kv = dts.symbol_types.find(func.guessed_name.function_name);
|
||||
if (kv != dts.symbol_types.end() && kv->second.arg_count() >= 1) {
|
||||
if (kv->second.base_type() != "function") {
|
||||
spdlog::error("Found a function named {} but the symbol has type {}",
|
||||
func.guessed_name.to_string(), kv->second.print());
|
||||
assert(false);
|
||||
}
|
||||
// GOOD!
|
||||
spdlog::info("Type Analysis on {} {}", func.guessed_name.to_string(),
|
||||
kv->second.print());
|
||||
func.run_type_analysis(kv->second, dts, data.linked_data);
|
||||
if (func.has_typemaps()) {
|
||||
successful_type_analysis++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
asm_funcs++;
|
||||
}
|
||||
@@ -670,8 +699,10 @@ void ObjectFileDB::analyze_functions() {
|
||||
int successful_basic_ops = total_basic_ops - total_failed_basic_ops;
|
||||
spdlog::info(" {}/{} basic ops converted successfully ({}%)", successful_basic_ops,
|
||||
total_basic_ops, 100.f * float(successful_basic_ops) / float(total_basic_ops));
|
||||
spdlog::info(" {}/{} cfgs converted to ir ({}%)\n", successful_cfg_irs, non_asm_funcs,
|
||||
spdlog::info(" {}/{} cfgs converted to ir ({}%)", successful_cfg_irs, non_asm_funcs,
|
||||
100.f * float(successful_cfg_irs) / float(non_asm_funcs));
|
||||
spdlog::info(" {}/{} functions passed type analysis ({:.2f}%)\n", successful_type_analysis,
|
||||
non_asm_funcs, 100.f * float(successful_type_analysis) / float(non_asm_funcs));
|
||||
|
||||
// for (auto& kv : unresolved_by_length) {
|
||||
// printf("LEN %d\n", kv.first);
|
||||
|
||||
Reference in New Issue
Block a user