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:
water111
2020-10-18 15:44:19 -04:00
committed by GitHub
parent 3e798cd3aa
commit fc1a8f37c6
19 changed files with 1064 additions and 36 deletions
+31 -3
View File
@@ -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";
}
+32 -1
View File
@@ -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);