[decompiler] Clean up config more (#458)

* remove global config

* fix dir
This commit is contained in:
water111
2021-05-11 20:49:54 -04:00
committed by GitHub
parent d7d563814f
commit 0599d144f8
17 changed files with 926 additions and 690 deletions
+1 -2
View File
@@ -680,8 +680,7 @@ std::string LinkedObjectFile::print_asm_function_disassembly(const std::string&
/*!
* Print disassembled functions and data segments.
*/
std::string LinkedObjectFile::print_disassembly() {
bool write_hex = get_config().write_hex_near_instructions;
std::string LinkedObjectFile::print_disassembly(bool write_hex) {
std::string result;
assert(segments <= 3);
+1 -1
View File
@@ -51,7 +51,7 @@ class LinkedObjectFile {
void disassemble_functions();
void process_fp_relative_links();
std::string print_scripts();
std::string print_disassembly();
std::string print_disassembly(bool write_hex);
bool has_any_functions();
void append_word_to_string(std::string& dest, const LinkedWord& word) const;
std::string print_function_disassembly(Function& func,
@@ -609,7 +609,8 @@ static void link_v5(LinkedObjectFile& f,
static void link_v3(LinkedObjectFile& f,
const std::vector<uint8_t>& data,
const std::string& name,
DecompilerTypeSystem& dts) {
DecompilerTypeSystem& dts,
int game_version) {
auto header = (const LinkHeaderV3*)(&data.at(0));
assert(name == header->name);
assert(header->segments == 3);
@@ -656,11 +657,11 @@ static void link_v3(LinkedObjectFile& f,
// HACK!
// why is this a thing?
// HACK!
if (get_config().game_version == 1 && name == "level-h" && seg_id == 0) {
if (game_version == 1 && name == "level-h" && seg_id == 0) {
segment_size++;
}
if (get_config().game_version == 2) {
if (game_version == 2) {
bool adjusted = false;
while (segment_size % 4) {
segment_size++;
@@ -800,14 +801,15 @@ static void link_v3(LinkedObjectFile& f,
*/
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data,
const std::string& name,
DecompilerTypeSystem& dts) {
DecompilerTypeSystem& dts,
int game_version) {
LinkedObjectFile result;
const auto* header = (const LinkHeaderCommon*)&data.at(0);
// use appropriate linker
if (header->version == 3) {
assert(header->type_tag == 0);
link_v3(result, data, name, dts);
link_v3(result, data, name, dts, game_version);
} else if (header->version == 4 || header->version == 2) {
assert(header->type_tag == 0xffffffff);
link_v2_or_v4(result, data, name, dts);
@@ -6,16 +6,12 @@
* This implements a decoder for the GOAL linking format.
*/
#ifndef NEXT_LINKEDOBJECTFILECREATION_H
#define NEXT_LINKEDOBJECTFILECREATION_H
#include "LinkedObjectFile.h"
namespace decompiler {
class DecompilerTypeSystem;
LinkedObjectFile to_linked_object_file(const std::vector<uint8_t>& data,
const std::string& name,
DecompilerTypeSystem& dts);
DecompilerTypeSystem& dts,
int game_version);
} // namespace decompiler
#endif // NEXT_LINKEDOBJECTFILECREATION_H
+24 -18
View File
@@ -110,7 +110,8 @@ ObjectFileData& ObjectFileDB::lookup_record(const ObjectFileRecord& rec) {
ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos,
const std::string& obj_file_name_map_file,
const std::vector<std::string>& object_files,
const std::vector<std::string>& str_files) {
const std::vector<std::string>& str_files,
const Config& config) {
Timer timer;
lg::info("-Loading types...");
@@ -128,14 +129,14 @@ ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos,
lg::info("-Loading {} DGOs...", _dgos.size());
for (auto& dgo : _dgos) {
get_objs_from_dgo(dgo);
get_objs_from_dgo(dgo, config);
}
lg::info("-Loading {} plain object files...", object_files.size());
for (auto& obj : object_files) {
auto data = file_util::read_binary_file(obj);
auto name = obj_filename_to_name(obj);
add_obj_from_dgo(name, name, data.data(), data.size(), "NO-XGO");
add_obj_from_dgo(name, name, data.data(), data.size(), "NO-XGO", config);
}
lg::info("-Loading {} streaming object files...", str_files.size());
@@ -149,7 +150,7 @@ ObjectFileDB::ObjectFileDB(const std::vector<std::string>& _dgos,
// append the chunk ID to the full name
std::string name = obj_name + fmt::format("+{}", i);
auto& data = reader.get_chunk(i);
add_obj_from_dgo(name, name, data.data(), data.size(), "NO-XGO");
add_obj_from_dgo(name, name, data.data(), data.size(), "NO-XGO", config);
}
}
@@ -190,7 +191,7 @@ constexpr int MAX_CHUNK_SIZE = 0x8000;
/*!
* Load the objects stored in the given DGO into the ObjectFileDB
*/
void ObjectFileDB::get_objs_from_dgo(const std::string& filename) {
void ObjectFileDB::get_objs_from_dgo(const std::string& filename, const Config& config) {
auto dgo_data = file_util::read_binary_file(filename);
stats.total_dgo_bytes += dgo_data.size();
@@ -221,7 +222,8 @@ void ObjectFileDB::get_objs_from_dgo(const std::string& filename) {
auto name = get_object_file_name(obj_header.name, reader.here(), obj_header.object_count);
add_obj_from_dgo(name, obj_header.name, reader.here(), obj_header.object_count, dgo_base_name);
add_obj_from_dgo(name, obj_header.name, reader.here(), obj_header.object_count, dgo_base_name,
config);
reader.ffwd(obj_header.object_count);
}
@@ -236,8 +238,8 @@ void ObjectFileDB::add_obj_from_dgo(const std::string& obj_name,
const std::string& name_in_dgo,
const uint8_t* obj_data,
uint32_t obj_size,
const std::string& dgo_name) {
const auto& config = get_config();
const std::string& dgo_name,
const Config& config) {
if (!config.allowed_objects.empty()) {
if (config.allowed_objects.find(obj_name) == config.allowed_objects.end()) {
return;
@@ -390,14 +392,14 @@ std::string ObjectFileDB::generate_obj_listing() {
/*!
* Process all of the linking data of all objects.
*/
void ObjectFileDB::process_link_data() {
void ObjectFileDB::process_link_data(const Config& config) {
lg::info("Processing Link Data...");
Timer process_link_timer;
LinkedObjectFile::Stats combined_stats;
for_each_obj([&](ObjectFileData& obj) {
obj.linked_data = to_linked_object_file(obj.data, obj.record.name, dts);
obj.linked_data = to_linked_object_file(obj.data, obj.record.name, dts, config.game_version);
combined_stats.add(obj.linked_data.stats);
});
@@ -454,7 +456,8 @@ void ObjectFileDB::write_object_file_words(const std::string& output_dir,
*/
void ObjectFileDB::write_disassembly(const std::string& output_dir,
bool disassemble_data,
bool disassemble_code) {
bool disassemble_code,
bool print_hex) {
lg::info("- Writing functions...");
Timer timer;
uint32_t total_bytes = 0, total_files = 0;
@@ -463,7 +466,7 @@ void ObjectFileDB::write_disassembly(const std::string& output_dir,
for_each_obj([&](ObjectFileData& obj) {
if ((obj.obj_version == 3 && disassemble_code) || (obj.obj_version != 3 && disassemble_data)) {
auto file_text = obj.linked_data.print_disassembly();
auto file_text = obj.linked_data.print_disassembly(print_hex);
asm_functions += obj.linked_data.print_asm_function_disassembly(obj.to_unique_name());
auto file_name = file_util::combine_path(output_dir, obj.to_unique_name() + ".asm");
@@ -488,7 +491,7 @@ void ObjectFileDB::write_disassembly(const std::string& output_dir,
/*!
* Find code/data zones, identify functions, and disassemble
*/
void ObjectFileDB::find_code() {
void ObjectFileDB::find_code(const Config& config) {
lg::info("Finding code in object files...");
LinkedObjectFile::Stats combined_stats;
Timer timer;
@@ -499,7 +502,7 @@ void ObjectFileDB::find_code() {
obj.linked_data.find_functions();
obj.linked_data.disassemble_functions();
if (get_config().game_version == 1 || obj.to_unique_name() != "effect-control-v0") {
if (config.game_version == 1 || obj.to_unique_name() != "effect-control-v0") {
obj.linked_data.process_fp_relative_links();
} else {
lg::warn("Skipping process_fp_relative_links in {}", obj.to_unique_name().c_str());
@@ -619,12 +622,11 @@ std::string ObjectFileDB::process_game_count_file() {
/*!
* This is the main decompiler routine which runs after we've identified functions.
*/
void ObjectFileDB::analyze_functions_ir1() {
void ObjectFileDB::analyze_functions_ir1(const Config& config) {
lg::info("- Analyzing Functions...");
Timer timer;
int total_functions = 0;
const auto& config = get_config();
// Step 1 - analyze the "top level" or "login" code for each object file.
// this will give us type definitions, method definitions, and function definitions...
@@ -665,7 +667,8 @@ void ObjectFileDB::analyze_functions_ir1() {
unique_names.insert(name);
if (config.asm_functions_by_name.find(name) != config.asm_functions_by_name.end()) {
if (config.hacks.asm_functions_by_name.find(name) !=
config.hacks.asm_functions_by_name.end()) {
func.warnings.info("Flagged as asm by config");
func.suspected_asm = true;
}
@@ -735,7 +738,10 @@ void ObjectFileDB::analyze_functions_ir1() {
// if we got an inspect method, inspect it.
if (func.is_inspect_method) {
auto result = inspect_inspect_method(func, func.method_of_type, dts, data.linked_data);
auto result = inspect_inspect_method(
func, func.method_of_type, dts, data.linked_data,
config.hacks.types_with_bad_inspect_methods.find(func.method_of_type) !=
config.hacks.types_with_bad_inspect_methods.end());
all_type_defs += ";; " + data.to_unique_name() + "\n";
all_type_defs += result.print_as_deftype() + "\n";
}
+16 -12
View File
@@ -49,32 +49,34 @@ class ObjectFileDB {
ObjectFileDB(const std::vector<std::string>& _dgos,
const std::string& obj_file_name_map_file,
const std::vector<std::string>& object_files,
const std::vector<std::string>& str_files);
const std::vector<std::string>& str_files,
const Config& config);
std::string generate_dgo_listing();
std::string generate_obj_listing();
void process_link_data();
void process_link_data(const Config& config);
void process_labels();
void find_code();
void find_code(const Config& config);
void find_and_write_scripts(const std::string& output_dir);
void dump_raw_objects(const std::string& output_dir);
void write_object_file_words(const std::string& output_dir, bool dump_data, bool dump_code);
void write_disassembly(const std::string& output_dir,
bool disassemble_data,
bool disassemble_code);
bool disassemble_code,
bool print_hex);
void analyze_functions_ir1();
void analyze_functions_ir2(const std::string& output_dir);
void ir2_top_level_pass();
void analyze_functions_ir1(const Config& config);
void analyze_functions_ir2(const std::string& output_dir, const Config& config);
void ir2_top_level_pass(const Config& config);
void ir2_stack_spill_slot_pass();
void ir2_basic_block_pass();
void ir2_atomic_op_pass();
void ir2_type_analysis_pass();
void ir2_atomic_op_pass(const Config& config);
void ir2_type_analysis_pass(const Config& config);
void ir2_register_usage_pass();
void ir2_variable_pass();
void ir2_cfg_build_pass();
void ir2_store_current_forms();
void ir2_build_expressions();
void ir2_build_expressions(const Config& config);
void ir2_insert_lets();
void ir2_rewrite_inline_asm_instructions();
void ir2_insert_anonymous_functions();
@@ -94,16 +96,18 @@ class ObjectFileDB {
bool lookup_function_type(const FunctionName& name,
const std::string& obj_name,
const Config& config,
TypeSpec* result);
public:
void load_map_file(const std::string& map_data);
void get_objs_from_dgo(const std::string& filename);
void get_objs_from_dgo(const std::string& filename, const Config& config);
void add_obj_from_dgo(const std::string& obj_name,
const std::string& name_in_dgo,
const uint8_t* obj_data,
uint32_t obj_size,
const std::string& dgo_name);
const std::string& dgo_name,
const Config& config);
/*!
* Apply f to all ObjectFileData's. Does it in the right order.
+37 -28
View File
@@ -29,18 +29,18 @@ namespace decompiler {
* At this point, we assume that the files are loaded and we've run find_code to locate all
* functions, but nothing else.
*/
void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir) {
void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir, const Config& config) {
lg::info("Using IR2 analysis...");
lg::info("Processing top-level functions...");
ir2_top_level_pass();
ir2_top_level_pass(config);
lg::info("Processing basic blocks and control flow graph...");
ir2_basic_block_pass();
lg::info("Finding stack spills...");
ir2_stack_spill_slot_pass();
lg::info("Converting to atomic ops...");
ir2_atomic_op_pass();
ir2_atomic_op_pass(config);
lg::info("Running type analysis...");
ir2_type_analysis_pass();
ir2_type_analysis_pass(config);
lg::info("Register usage analysis...");
ir2_register_usage_pass();
lg::info("Variable analysis...");
@@ -51,7 +51,7 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir) {
lg::info("Storing temporary form result...");
ir2_store_current_forms();
lg::info("Expression building...");
ir2_build_expressions();
ir2_build_expressions(config);
lg::info("Re-writing inline asm instructions...");
ir2_rewrite_inline_asm_instructions();
@@ -74,7 +74,7 @@ void ObjectFileDB::analyze_functions_ir2(const std::string& output_dir) {
* - Find method definitions
* - Warn for non-unique function names.
*/
void ObjectFileDB::ir2_top_level_pass() {
void ObjectFileDB::ir2_top_level_pass(const Config& config) {
Timer timer;
int total_functions = 0;
int total_named_global_functions = 0;
@@ -134,8 +134,8 @@ void ObjectFileDB::ir2_top_level_pass() {
unique_names.insert(name);
if (get_config().asm_functions_by_name.find(name) !=
get_config().asm_functions_by_name.end()) {
if (config.hacks.asm_functions_by_name.find(name) !=
config.hacks.asm_functions_by_name.end()) {
func.warnings.info("Flagged as asm by config");
func.suspected_asm = true;
}
@@ -253,7 +253,7 @@ void ObjectFileDB::ir2_stack_spill_slot_pass() {
* Conversion of MIPS instructions into AtomicOps. The AtomicOps represent what we
* think are IR of the original GOAL compiler.
*/
void ObjectFileDB::ir2_atomic_op_pass() {
void ObjectFileDB::ir2_atomic_op_pass(const Config& config) {
Timer timer;
int total_functions = 0;
int attempted = 0;
@@ -266,8 +266,8 @@ void ObjectFileDB::ir2_atomic_op_pass() {
attempted++;
try {
bool inline_asm =
get_config().hint_inline_assembly_functions.find(func.guessed_name.to_string()) !=
get_config().hint_inline_assembly_functions.end();
config.hacks.hint_inline_assembly_functions.find(func.guessed_name.to_string()) !=
config.hacks.hint_inline_assembly_functions.end();
auto ops = convert_function_to_atomic_ops(func, data.linked_data.labels, func.warnings,
inline_asm);
func.ir2.atomic_ops = std::make_shared<FunctionAtomicOps>(std::move(ops));
@@ -288,13 +288,23 @@ void ObjectFileDB::ir2_atomic_op_pass() {
100.f * attempted / total_functions, 100.f * successful / attempted);
}
template <typename Key, typename Value>
Value try_lookup(const std::unordered_map<Key, Value>& map, const Key& key) {
auto lookup = map.find(key);
if (lookup == map.end()) {
return Value();
} else {
return lookup->second;
}
}
/*!
* Analyze registers and determine the type in each register at each instruction.
* - Figure out the type of each function, from configs.
* - Propagate types.
* - NOTE: this will update register info usage more accurately for functions.
*/
void ObjectFileDB::ir2_type_analysis_pass() {
void ObjectFileDB::ir2_type_analysis_pass(const Config& config) {
Timer timer;
int total_functions = 0;
int non_asm_functions = 0;
@@ -307,21 +317,21 @@ void ObjectFileDB::ir2_type_analysis_pass() {
if (!func.suspected_asm) {
non_asm_functions++;
TypeSpec ts;
if (lookup_function_type(func.guessed_name, data.to_unique_name(), &ts) &&
if (lookup_function_type(func.guessed_name, data.to_unique_name(), config, &ts) &&
func.ir2.atomic_ops_succeeded) {
func.type = ts;
attempted_functions++;
// try type analysis here.
auto func_name = func.guessed_name.to_string();
auto casts = get_config().type_casts_by_function_by_atomic_op_idx[func_name];
auto label_types = get_config().label_types[data.to_unique_name()];
auto casts = try_lookup(config.type_casts_by_function_by_atomic_op_idx, func_name);
auto label_types = try_lookup(config.label_types, data.to_unique_name());
func.ir2.env.set_type_casts(casts);
func.ir2.env.set_label_types(label_types);
if (get_config().pair_functions_by_name.find(func_name) !=
get_config().pair_functions_by_name.end()) {
if (config.hacks.pair_functions_by_name.find(func_name) !=
config.hacks.pair_functions_by_name.end()) {
func.ir2.env.set_sloppy_pair_typing();
}
func.ir2.env.set_stack_var_hints(get_config().stack_var_hints_by_function[func_name]);
func.ir2.env.set_stack_var_hints(try_lookup(config.stack_var_hints_by_function, func_name));
if (run_type_analysis_ir2(ts, dts, func)) {
successful_functions++;
func.ir2.env.types_succeeded = true;
@@ -436,7 +446,7 @@ void ObjectFileDB::ir2_store_current_forms() {
lg::info("Stored debug forms for {} functions in {:.2f} ms\n", total, timer.getMs());
}
void ObjectFileDB::ir2_build_expressions() {
void ObjectFileDB::ir2_build_expressions(const Config& config) {
Timer timer;
int total = 0;
int attempted = 0;
@@ -449,13 +459,13 @@ void ObjectFileDB::ir2_build_expressions() {
func.ir2.env.types_succeeded) {
attempted++;
auto name = func.guessed_name.to_string();
auto arg_config = get_config().function_arg_names.find(name);
auto var_config = get_config().function_var_overrides.find(name);
auto arg_config = config.function_arg_names.find(name);
auto var_config = config.function_var_overrides.find(name);
if (convert_to_expressions(func.ir2.top_form, *func.ir2.form_pool, func,
arg_config != get_config().function_arg_names.end()
arg_config != config.function_arg_names.end()
? arg_config->second
: std::vector<std::string>{},
var_config != get_config().function_var_overrides.end()
var_config != config.function_var_overrides.end()
? var_config->second
: std::unordered_map<std::string, LocalVarOverride>{},
dts)) {
@@ -826,12 +836,11 @@ std::string ObjectFileDB::ir2_function_to_string(ObjectFileData& data, Function&
*/
bool ObjectFileDB::lookup_function_type(const FunctionName& name,
const std::string& obj_name,
const Config& config,
TypeSpec* result) {
auto& cfg = get_config();
// don't return function types that are explictly flagged as bad in config.
if (cfg.no_type_analysis_functions_by_name.find(name.to_string()) !=
cfg.no_type_analysis_functions_by_name.end()) {
if (config.hacks.no_type_analysis_functions_by_name.find(name.to_string()) !=
config.hacks.no_type_analysis_functions_by_name.end()) {
return false;
}
@@ -867,7 +876,7 @@ bool ObjectFileDB::lookup_function_type(const FunctionName& name,
return true;
} else if (name.kind == FunctionName::FunctionKind::UNIDENTIFIED) {
// try looking up the object
const auto& map = get_config().anon_function_types_by_obj_by_id;
const auto& map = config.anon_function_types_by_obj_by_id;
auto obj_kv = map.find(obj_name);
if (obj_kv != map.end()) {
auto func_kv = obj_kv->second.find(name.get_anon_id());