[Decompiler, Game] Run type prop on gstring (#168)

* run type prop on gstring

* add types-h

* new settings

* cmake switch

* down to 4 failing functions

* clang format
This commit is contained in:
water111
2020-12-26 11:09:59 -05:00
committed by GitHub
parent 6bf9d97c51
commit 25301a8bbc
24 changed files with 595 additions and 160 deletions
+6 -1
View File
@@ -1,5 +1,10 @@
# Top Level CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
if (UNIX)
cmake_minimum_required(VERSION 3.10)
else ()
cmake_minimum_required(VERSION 3.16)
endif ()
project(jak)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
+4
View File
@@ -732,6 +732,10 @@ void TypeSystem::add_builtin_types() {
builtin_structure_inherit(string_type);
add_field_to_type(string_type, "allocated-length", make_typespec("int32")); // todo integer type
add_field_to_type(string_type, "data", make_typespec("uint8"), false, true); // todo integer type
// string is never deftype'd for the decompiler, so we need to manually give the constructor
// type here.
add_method(string_type, "new",
make_function_typespec({"symbol", "type", "int", "string"}, "_type_"));
// FUNCTION
builtin_structure_inherit(function_type);
+5
View File
@@ -50,6 +50,11 @@ struct FunctionName {
}
}
int get_anon_id() const {
assert(kind == FunctionKind::UNIDENTIFIED);
return id_in_object;
}
bool empty() const { return kind == FunctionKind::UNIDENTIFIED; }
void set_as_top_level() { kind = FunctionKind::TOP_LEVEL_INIT; }
+3
View File
@@ -15,6 +15,9 @@ TypeState construct_initial_typestate(const TypeSpec& f_ts) {
auto reg_type = f_ts.get_arg(i);
result.gpr_types[reg_id] = TP_Type::make_from_typespec(reg_type);
}
// todo, more specific process types for behaviors.
result.gpr_types[Reg::S6] = TP_Type::make_from_typespec(TypeSpec("process"));
return result;
}
+47 -9
View File
@@ -12,6 +12,7 @@
#include "decompiler/Function/BasicBlocks.h"
#include "decompiler/Disasm/InstructionMatching.h"
#include "decompiler/IR/IR.h"
#include "common/symbols.h"
namespace {
@@ -753,6 +754,13 @@ std::shared_ptr<IR_Atomic> try_daddiu(Instruction& instr, int idx) {
op->write_regs.push_back(instr.get_dst(0).get_reg());
op->reg_info_set = true;
return op;
} else if (instr.kind == InstructionKind::DADDIU && instr.get_src(0).is_reg(make_gpr(Reg::S7)) &&
instr.get_src(1).is_imm() && instr.get_src(1).get_imm() == FIX_SYM_TRUE) {
auto op = make_set_atomic(IR_Set_Atomic::REG_64, make_reg(instr.get_dst(0).get_reg(), idx),
std::make_shared<IR_Symbol>("#t"));
op->write_regs.push_back(instr.get_dst(0).get_reg());
op->reg_info_set = true;
return op;
} else if (instr.kind == InstructionKind::DADDIU && instr.get_src(0).is_reg(make_gpr(Reg::FP)) &&
instr.get_src(1).kind == InstructionAtom::LABEL) {
auto op = make_set_atomic(IR_Set_Atomic::REG_64, make_reg(instr.get_dst(0).get_reg(), idx),
@@ -1032,14 +1040,42 @@ std::shared_ptr<IR_Atomic> try_sw(Instruction& instr, int idx) {
std::shared_ptr<IR_Atomic> try_sb(Instruction& instr, int idx) {
if (instr.kind == InstructionKind::SB && instr.get_src(1).is_imm()) {
auto op = std::make_shared<IR_Store_Atomic>(
IR_Store_Atomic::INTEGER,
std::make_shared<IR_IntMath2>(
IR_IntMath2::ADD, make_reg(instr.get_src(2).get_reg(), idx),
std::make_shared<IR_IntegerConstant>(instr.get_src(1).get_imm())),
make_reg(instr.get_src(0).get_reg(), idx), 1);
op->update_reginfo_self(0, 2, 0);
return op;
if (instr.get_src(1).get_imm() == 0) {
if (instr.get_src(0).is_reg(make_gpr(Reg::R0))) {
auto op = std::make_shared<IR_Store_Atomic>(IR_Store_Atomic::INTEGER,
make_reg(instr.get_src(2).get_reg(), idx),
std::make_shared<IR_IntegerConstant>(0), 1);
op->update_reginfo_self(0, 1, 0);
return op;
} else {
auto op = std::make_shared<IR_Store_Atomic>(IR_Store_Atomic::INTEGER,
make_reg(instr.get_src(2).get_reg(), idx),
make_reg(instr.get_src(0).get_reg(), idx), 1);
op->update_reginfo_self(0, 2, 0);
return op;
}
} else {
if (instr.get_src(0).is_reg(make_gpr(Reg::R0))) {
auto op = std::make_shared<IR_Store_Atomic>(
IR_Store_Atomic::INTEGER,
std::make_shared<IR_IntMath2>(
IR_IntMath2::ADD, make_reg(instr.get_src(2).get_reg(), idx),
std::make_shared<IR_IntegerConstant>(instr.get_src(1).get_imm())),
std::make_shared<IR_IntegerConstant>(0), 1);
op->update_reginfo_self(0, 1, 0);
return op;
} else {
auto op = std::make_shared<IR_Store_Atomic>(
IR_Store_Atomic::INTEGER,
std::make_shared<IR_IntMath2>(
IR_IntMath2::ADD, make_reg(instr.get_src(2).get_reg(), idx),
std::make_shared<IR_IntegerConstant>(instr.get_src(1).get_imm())),
make_reg(instr.get_src(0).get_reg(), idx), 1);
op->update_reginfo_self(0, 2, 0);
return op;
}
}
}
return nullptr;
}
@@ -1169,6 +1205,7 @@ std::shared_ptr<IR_Atomic> try_movn(Instruction& instr, int idx) {
op->write_regs.push_back(dst);
op->read_regs.push_back(src);
op->reg_info_set = true;
return op;
}
return nullptr;
}
@@ -1182,6 +1219,7 @@ std::shared_ptr<IR_Atomic> try_movz(Instruction& instr, int idx) {
op->write_regs.push_back(dst);
op->read_regs.push_back(src);
op->reg_info_set = true;
return op;
}
return nullptr;
}
@@ -1982,7 +2020,7 @@ std::shared_ptr<IR_Atomic> try_lwu(Instruction& i0,
i2.get_src(0).get_imm() == 12 && i2.get_src(1).is_reg(s6) &&
i3.kind == InstructionKind::JALR && i3.get_dst(0).is_reg(make_gpr(Reg::RA)) &&
i3.get_src(0).is_reg(s6) && i4.kind == InstructionKind::MFLO1 && i4.get_dst(0).is_reg(s6)) {
auto op = std::make_shared<IR_Suspend>();
auto op = std::make_shared<IR_Suspend_Atomic>();
op->reg_info_set = true;
return op;
}
+2 -2
View File
@@ -975,7 +975,7 @@ void IR_Compare::get_children(std::vector<std::shared_ptr<IR>>* output) const {
condition.get_children(output);
}
goos::Object IR_Suspend::to_form(const LinkedObjectFile& file) const {
goos::Object IR_Suspend_Atomic::to_form(const LinkedObjectFile& file) const {
(void)file;
return pretty_print::build_list("suspend!");
}
@@ -984,7 +984,7 @@ void IR_Nop::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
void IR_Suspend::get_children(std::vector<std::shared_ptr<IR>>* output) const {
void IR_Suspend_Atomic::get_children(std::vector<std::shared_ptr<IR>>* output) const {
(void)output;
}
+11 -5
View File
@@ -278,9 +278,9 @@ class IR_FloatMath1 : public virtual IR {
std::shared_ptr<IR> arg;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
// TP_Type get_expression_type(const TypeState& input,
// const LinkedObjectFile& file,
// DecompilerTypeSystem& dts) override;
TP_Type get_expression_type(const TypeState& input,
const LinkedObjectFile& file,
DecompilerTypeSystem& dts) override;
};
class IR_IntMath2 : public virtual IR {
@@ -505,11 +505,14 @@ class IR_Nop_Atomic : public IR_Nop, public IR_Atomic {
DecompilerTypeSystem& dts) override;
};
class IR_Suspend : public virtual IR, public IR_Atomic {
class IR_Suspend_Atomic : public virtual IR, public IR_Atomic {
public:
IR_Suspend() = default;
IR_Suspend_Atomic() = default;
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
void propagate_types(const TypeState& input,
const LinkedObjectFile& file,
DecompilerTypeSystem& dts) override;
};
class IR_Breakpoint_Atomic : public virtual IR_Atomic {
@@ -668,6 +671,9 @@ class IR_CMoveF : public virtual IR {
: src(std::move(_src)), on_zero(_on_zero) {}
goos::Object to_form(const LinkedObjectFile& file) const override;
void get_children(std::vector<std::shared_ptr<IR>>* output) const override;
TP_Type get_expression_type(const TypeState& input,
const LinkedObjectFile& file,
DecompilerTypeSystem& dts) override;
};
class IR_AsmReg : public virtual IR {
+55 -28
View File
@@ -367,26 +367,26 @@ TP_Type IR_FloatMath2::get_expression_type(const TypeState& input,
}
}
// TP_Type IR_FloatMath1::get_expression_type(const TypeState& input,
// const LinkedObjectFile& file,
// DecompilerTypeSystem& dts) {
// (void)input;
// (void)file;
// (void)dts;
// // FLOAT_TO_INT, INT_TO_FLOAT, ABS, NEG, SQRT
// switch (kind) {
// case FLOAT_TO_INT:
// return TP_Type(TypeSpec("int"));
// case INT_TO_FLOAT:
// case ABS:
// case NEG:
// case SQRT:
// return TP_Type(TypeSpec("float"));
// default:
// assert(false);
// }
//}
//
TP_Type IR_FloatMath1::get_expression_type(const TypeState& input,
const LinkedObjectFile& file,
DecompilerTypeSystem& dts) {
(void)input;
(void)file;
(void)dts;
// FLOAT_TO_INT, INT_TO_FLOAT, ABS, NEG, SQRT
switch (kind) {
case FLOAT_TO_INT:
return TP_Type::make_from_typespec(TypeSpec("int"));
case INT_TO_FLOAT:
case ABS:
case NEG:
case SQRT:
return TP_Type::make_from_typespec(TypeSpec("float"));
default:
assert(false);
}
}
TP_Type IR_IntMath2::get_expression_type(const TypeState& input,
const LinkedObjectFile& file,
DecompilerTypeSystem& dts) {
@@ -534,24 +534,30 @@ TP_Type IR_IntMath2::get_expression_type(const TypeState& input,
return TP_Type::make_object_plus_product(arg0_type.typespec(), arg1_type.get_multiplier());
}
if (kind == ADD && arg0_type.typespec() == TypeSpec("pointer") &&
if (kind == ADD && arg0_type.typespec().base_type() == "pointer" &&
tc(dts, TypeSpec("integer"), arg1_type)) {
// plain pointer plus integer = plain pointer
return TP_Type::make_from_typespec(TypeSpec("pointer"));
}
if (kind == ADD && arg1_type.typespec() == TypeSpec("pointer") &&
if (kind == ADD && arg1_type.typespec().base_type() == "pointer" &&
tc(dts, TypeSpec("integer"), arg0_type)) {
// plain pointer plus integer = plain pointer
return TP_Type::make_from_typespec(TypeSpec("pointer"));
}
// byte access of offset array field trick.
// arg1 holds a structure.
// arg0 is an integer in a register.
if (tc(dts, TypeSpec("structure"), arg1_type) && !dynamic_cast<IR_IntegerConstant*>(arg0.get()) &&
is_int_or_uint(dts, arg0_type)) {
return TP_Type::make_object_plus_product(arg1_type.typespec(), 1);
if (arg1_type.typespec() == TypeSpec("symbol") &&
arg0_type.is_integer_constant(SYM_INFO_OFFSET + POINTER_SIZE)) {
// symbol -> GOAL String
return TP_Type::make_from_typespec(dts.ts.make_pointer_typespec("string"));
} else {
// byte access of offset array field trick.
// arg1 holds a structure.
// arg0 is an integer in a register.
return TP_Type::make_object_plus_product(arg1_type.typespec(), 1);
}
}
if (kind == AND) {
@@ -687,7 +693,7 @@ TP_Type IR_IntMath1::get_expression_type(const TypeState& input,
}
throw std::runtime_error("IR_IntMath1::get_expression_type case not handled: " +
to_form(file).print());
to_form(file).print() + " " + arg_type.print());
}
TP_Type IR_SymbolValue::get_expression_type(const TypeState& input,
@@ -760,6 +766,14 @@ void IR_Nop_Atomic::propagate_types(const TypeState& input,
end_types = input;
}
void IR_Suspend_Atomic::propagate_types(const TypeState& input,
const LinkedObjectFile& file,
DecompilerTypeSystem& dts) {
(void)file;
(void)dts;
end_types = input;
}
void IR_Call_Atomic::propagate_types(const TypeState& input,
const LinkedObjectFile& file,
DecompilerTypeSystem& dts) {
@@ -871,8 +885,10 @@ TP_Type IR_StaticAddress::get_expression_type(const TypeState& input,
(void)input;
(void)dts;
// todo - we should map out static data and use a real type system lookup here.
auto label = file.labels.at(label_id);
if ((label.offset & 0xf) == 4) {
// strings are 16-byte aligned, but functions are 8 byte aligned?
if ((label.offset & 7) == BASIC_OFFSET) {
// it's a basic! probably.
const auto& word = file.words_by_seg.at(label.target_segment).at((label.offset - 4) / 4);
if (word.kind == LinkedWord::TYPE_PTR) {
@@ -883,6 +899,8 @@ TP_Type IR_StaticAddress::get_expression_type(const TypeState& input,
return TP_Type::make_from_typespec(TypeSpec(word.symbol_name));
}
}
} else if ((label.offset & 7) == PAIR_OFFSET) {
return TP_Type::make_from_typespec(TypeSpec("pair"));
}
throw std::runtime_error("IR_StaticAddress couldn't figure out the type: " + label.name);
@@ -918,4 +936,13 @@ TP_Type IR_EmptyPair::get_expression_type(const TypeState& input,
(void)dts;
// GOAL's empty pair is actually a pair type, containing the empty pair as the car and cdr
return TP_Type::make_from_typespec(TypeSpec("pair"));
}
TP_Type IR_CMoveF::get_expression_type(const TypeState& input,
const LinkedObjectFile& file,
DecompilerTypeSystem& dts) {
(void)input;
(void)file;
(void)dts;
return TP_Type::make_from_typespec(TypeSpec("symbol"));
}
+57 -2
View File
@@ -828,7 +828,7 @@ void ObjectFileDB::analyze_functions() {
if (duplicated_functions.find(name) != duplicated_functions.end()) {
duplicated_functions[name].insert(data.to_unique_name());
func.warnings += ";; this function exists in multiple non-identical object files";
func.warnings += ";; this function exists in multiple non-identical object files\n";
}
});
/*
@@ -851,6 +851,7 @@ void ObjectFileDB::analyze_functions() {
int successful_cfg_irs = 0;
int successful_type_analysis = 0;
int attempted_type_analysis = 0;
int bad_type_analysis = 0; // didn't attempt because we didn't know how + attempted but failed
std::map<int, std::vector<std::string>> unresolved_by_length;
@@ -941,7 +942,13 @@ void ObjectFileDB::analyze_functions() {
// kv->second.print());
if (func.run_type_analysis(kv->second, dts, data.linked_data, hints)) {
successful_type_analysis++;
} else {
// bad, failed.
bad_type_analysis++;
}
} else {
// bad, don't know global type
bad_type_analysis++;
}
} else if (func.guessed_name.kind == FunctionName::FunctionKind::METHOD) {
// it's a method.
@@ -963,12 +970,56 @@ void ObjectFileDB::analyze_functions() {
// func.type.print());
if (func.run_type_analysis(func.type, dts, data.linked_data, hints)) {
successful_type_analysis++;
} else {
bad_type_analysis++;
}
} else {
// not enough type info
bad_type_analysis++;
}
} catch (std::runtime_error& e) {
// failed to lookup method info
bad_type_analysis++;
}
} else if (func.guessed_name.kind == FunctionName::FunctionKind::TOP_LEVEL_INIT) {
attempted_type_analysis++;
func.type = dts.ts.make_function_typespec({}, "none");
func.attempted_type_analysis = true;
if (func.run_type_analysis(func.type, dts, data.linked_data, hints)) {
successful_type_analysis++;
} else {
// failed
bad_type_analysis++;
}
} else if (func.guessed_name.kind == FunctionName::FunctionKind::UNIDENTIFIED) {
auto obj_name = data.to_unique_name();
// try looking up the object
const auto& map = get_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(func.guessed_name.get_anon_id());
if (func_kv != obj_kv->second.end()) {
attempted_type_analysis++;
func.type = dts.parse_type_spec(func_kv->second);
func.attempted_type_analysis = true;
if (func.run_type_analysis(func.type, dts, data.linked_data, hints)) {
successful_type_analysis++;
} else {
// tried, but failed.
bad_type_analysis++;
}
} else {
// no id
bad_type_analysis++;
}
} else {
// no object in map
bad_type_analysis++;
}
} else {
// unsupported function kind
bad_type_analysis++;
}
if (!func.attempted_type_analysis) {
@@ -1024,8 +1075,12 @@ void ObjectFileDB::analyze_functions() {
spdlog::info(" {}/{} functions that attempted type analysis succeeded ({:.2f}%)",
successful_type_analysis, attempted_type_analysis,
100.f * float(successful_type_analysis) / float(attempted_type_analysis));
spdlog::info(" {}/{} functions passed type analysis ({:.2f}%)\n", successful_type_analysis,
spdlog::info(" {}/{} functions passed type analysis ({:.2f}%)", successful_type_analysis,
non_asm_funcs, 100.f * float(successful_type_analysis) / float(non_asm_funcs));
spdlog::info(
" {} functions were supposed to do type analysis but either failed or didn't know their "
"types.\n",
bad_type_analysis);
// for (auto& kv : unresolved_by_length) {
// printf("LEN %d\n", kv.first);
+27 -4
View File
@@ -8,6 +8,21 @@ Config& get_config() {
return gConfig;
}
namespace {
/*!
* Read an entry from cfg containing the name of a json file, and parse that file.
* Relative to jak-project directory.
*/
nlohmann::json read_json_file_from_config(const nlohmann::json& cfg, const std::string& file_key) {
auto file_name = cfg.at(file_key).get<std::string>();
auto file_txt = file_util::read_text_file(file_util::get_file_path({file_name}));
return nlohmann::json::parse(file_txt, nullptr, true, true);
}
} // namespace
/*!
* Parse the main config file and set the global decompiler configuration.
*/
void set_config(const std::string& path_to_config_file) {
auto config_str = file_util::read_text_file(path_to_config_file);
// to ignore comments in json, which may be useful
@@ -59,10 +74,7 @@ void set_config(const std::string& path_to_config_file) {
gConfig.bad_inspect_types.insert(x);
}
auto type_hints_file_name = cfg.at("type_hints_file").get<std::string>();
auto type_hints_txt = file_util::read_text_file(file_util::get_file_path({type_hints_file_name}));
auto type_hints_json = nlohmann::json::parse(type_hints_txt, nullptr, true, true);
auto type_hints_json = read_json_file_from_config(cfg, "type_hints_file");
for (auto& kv : type_hints_json.items()) {
auto& function_name = kv.key();
auto& hints = kv.value();
@@ -77,4 +89,15 @@ void set_config(const std::string& path_to_config_file) {
}
}
}
auto anon_func_json = read_json_file_from_config(cfg, "anonymous_function_types_file");
for (auto& kv : anon_func_json.items()) {
auto& obj_file_name = kv.key();
auto& anon_types = kv.value();
for (auto& anon_type : anon_types) {
auto id = anon_type.at(0).get<int>();
const auto& type_name = anon_type.at(1).get<std::string>();
gConfig.anon_function_types_by_obj_by_id[obj_file_name][id] = type_name;
}
}
}
+2 -1
View File
@@ -40,7 +40,8 @@ struct Config {
std::unordered_set<std::string> no_type_analysis_functions_by_name;
std::unordered_map<std::string, std::unordered_map<int, std::vector<TypeHint>>>
type_hints_by_function_by_idx;
// ...
std::unordered_map<std::string, std::unordered_map<int, std::string>>
anon_function_types_by_obj_by_id;
};
Config& get_config();
+120 -86
View File
@@ -7,20 +7,17 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; to move
(define-extern name= (function basic basic symbol)) ;; gstring
(define-extern stop (function int)) ;; logic-target
(define-extern set-blackout-frames (function int int))
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; BUILT-IN / C TYPES ;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;; built-in
;; built-in. these need to be listed so the type symbols have type of type
;; in the decompiler.
(define-extern symbol type)
(define-extern object type)
(define-extern #f symbol)
@@ -37,17 +34,27 @@
(define-extern string type)
(define-extern uint8 type)
(define-extern int8 type)
(define-extern uint16 type)
(define-extern int16 type)
(define-extern uint32 type)
(define-extern int32 type)
(define-extern uint64 type)
(define-extern int64 type)
(define-extern uint128 type)
(define-extern int128 type)
(define-extern float type)
(define-extern nothing (function none))
(define-extern nothing (function none))
;; C
;; functions defined in C. TODO - this will end up being a duplicate of kernel-defs.gc?
(define-extern dgo-load (function string kheap int int none))
(define-extern *kernel-boot-message* symbol)
(define-extern *debug-segment* symbol)
(define-extern _format (function _varargs_ object))
(define-extern method-set! (function type int function none)) ;; may actually return function.
(define-extern *listener-function* (function object))
(define-extern *enable-method-set* int)
(define-extern install-debug-handler (function int object symbol))
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -75,7 +82,16 @@
(define-extern false-func (function symbol))
(define-extern true-func (function symbol))
(define-extern format (function _varargs_ object))
;; TODO - vec4s (or just forget it)
(define-extern vec4s type)
; ;; gcommon
; (deftype vec4s (uint128)
; ()
; :method-count-assert 9
; :size-assert #x10
; :flag-assert #x900000010
; ;; a bitfield type
; )
(deftype bfloat (basic)
((data float :offset-assert 4)
@@ -127,7 +143,7 @@
(define-extern print (function object object))
(define-extern printl (function object object))
(define-extern inspect (function object object))
(define-extern mem-print (function pointer int symbol))
(define-extern mem-print (function (pointer uint32) int symbol))
(define-extern *trace-list* pair)
(define-extern print-tree-bitmask (function int int symbol))
(define-extern valid? (function object type basic basic object symbol))
@@ -135,23 +151,7 @@
;; has issues:
(define-extern breakpoint-range-set! function)
; ;; gcommon
; (deftype array (UNKNOWN)
; ()
; :method-count-assert 0
; :size-assert #x0
; :flag-assert #x0
; ;; too many basic blocks
; )
; ;; gcommon
; (deftype vec4s (uint128)
; ()
; :method-count-assert 9
; :size-assert #x10
; :flag-assert #x900000010
; ;; likely a bitfield type
; )
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -270,7 +270,7 @@
(state state :offset-assert #x38)
(trans-hook function :offset-assert #x3c)
(post-hook function :offset-assert #x40)
(event-hook function :offset-assert #x44)
(event-hook (function stack-frame (function object) function state object) :offset-assert #x44)
(allocated-length int32 :offset-assert #x48)
(next-state basic :offset-assert #x4c)
(heap-base pointer :offset-assert #x50)
@@ -409,9 +409,9 @@
;; gkernel-h
(deftype state (protect-frame)
((code function :offset-assert 16)
(trans function :offset-assert 20)
(trans (function object) :offset-assert 20)
(post function :offset-assert 24)
(enter function :offset-assert 28)
(enter (function object object object object object object object) :offset-assert 28)
(event basic :offset-assert 32)
)
(:methods
@@ -470,7 +470,6 @@
(define-extern *debug-dead-pool* dead-pool-heap)
(define-extern *null-process* process)
(define-extern *vis-boot* basic)
(define-extern *stdcon* basic) ;; todo, more specific
(define-extern *global-search-name* basic)
(define-extern *global-search-count* int)
@@ -486,7 +485,10 @@
(define-extern search-process-tree (function process-tree (function process-tree object) process))
(define-extern execute-process-tree (function process-tree (function object object) kernel-context object))
(define-extern previous-brother (function process-tree process-tree))
(define-extern change-parent (function process-tree process-tree process-tree))
(define-extern change-brother (function process-tree process-tree process-tree))
(define-extern change-to-last-brother (function process-tree process-tree))
(define-extern *active-pool* process-tree)
(define-extern kernel-dispatcher (function (function object)))
@@ -520,7 +522,7 @@
(define-extern *camera-pool* process-tree)
(define-extern *nk-dead-pool* dead-pool-heap)
(define-extern change-to-last-brother function)
(define-extern *pickup-dead-pool* dead-pool)
(define-extern *camera-master-dead-pool* dead-pool)
(define-extern *camera-dead-pool* dead-pool)
@@ -549,11 +551,14 @@
;;(define-extern dead object) ;; unknown type
;; todo
(define-extern previous-brother function)
(define-extern change-brother function)
;;(define-extern *dead-pool-list* object) ;; unknown type
;; pskernel
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; PSKERNEL ;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(deftype lowmemmap (structure)
((irq-info-stack uint32 :offset-assert 0)
(irq2-info-stack uint32 :offset-assert 4)
@@ -575,6 +580,67 @@
:flag-assert #x900000044
)
(define-extern kernel-copy-function (function object object object object none))
(define-extern kernel-copy-to-kernel-ram (function none))
(define-extern kernel-write-function (function object object object none))
(define-extern kernel-write (function none))
(define-extern kernel-read-function (function object object none))
(define-extern kernel-read (function none))
(define-extern kernel-check-hardwired-addresses (function none))
(define-extern install-default-debug-handler (function object none))
(define-extern return-from-exception (function object none))
(define-extern kernel-set-exception-vector (function none))
(define-extern kernel-set-interrupt-vector (function none))
(define-extern kernel-set-level2-vector (function none))
(define-extern deinstall-debug-handler (function none))
(define-extern deinstall-debug-handlers (function none))
(define-extern resend-exception (function none))
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; GSTRING ;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(define-extern *string-tmp-str* string)
(define-extern *temp-string* string)
(define-extern *stdcon0* string)
(define-extern *stdcon1* string)
(define-extern *stdcon* string)
(define-extern *debug-draw-pauseable* symbol)
(define-extern copy-string<-string (function string string string))
(define-extern string= (function string string symbol))
(define-extern string-charp= (function string (pointer uint8) symbol))
(define-extern name= (function basic basic symbol))
(define-extern copyn-string<-charp (function string (pointer uint8) int string))
(define-extern string<-charp (function string (pointer uint8) string))
(define-extern charp<-string (function (pointer uint8) string int))
(define-extern copy-charp<-charp (function (pointer uint8) (pointer uint8) (pointer uint8)))
(define-extern cat-string<-string (function string string string))
(define-extern catn-string<-charp (function string (pointer uint8) int string))
(define-extern cat-string<-string_to_charp (function string string int (pointer uint8)))
(define-extern append-character-to-string (function string uint8 int))
(define-extern charp-basename (function (pointer uint8) (pointer uint8)))
(define-extern clear (function string string))
(define-extern string<? (function string string symbol))
(define-extern string>? (function string string symbol))
(define-extern string<=? (function string string symbol))
(define-extern string>=? (function string string symbol))
(define-extern string-skip-to-char (function (pointer uint8) uint8 (pointer uint8)))
;; this one might be wrong
(define-extern string-cat-to-last-char (function uint8 string uint8 (pointer uint8)))
(define-extern string-skip-whitespace (function (pointer uint8) (pointer uint8)))
(define-extern string-suck-up! (function string (pointer uint8) symbol))
(define-extern string-strip-leading-whitespace! (function string symbol))
(define-extern string-strip-trailing-whitespace! (function string symbol))
(define-extern string-strip-whitespace! (function string symbol))
(define-extern string-get-arg!! (function string string symbol))
(define-extern string->int (function string int))
(define-extern string->float (function string float))
(define-extern string-get-int32!! (function (pointer int32) string symbol))
(define-extern string-get-float!! (function (pointer float) string symbol))
(define-extern string-get-flag!! (function (pointer symbol) string string string symbol))
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; DGO-H ;;;;;;;;;;;;;;;;;;;
@@ -603,6 +669,26 @@
:flag-assert #x900000010
)
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; GSTATE ;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
(define-extern looping-code (function symbol))
(define-extern send-event-function (function process state object))
(define-extern enter-state (function object object object object object object object))
(define-extern inherit-state (function state state state))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;; END OF KERNEL.CGO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;; START OF GAME.CGO ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; MATH ;;;;;;;;;;;;;;;;;;;;;;
@@ -31215,61 +31301,9 @@
;; KERNEL
(define-extern install-default-debug-handler function)
(define-extern return-from-exception function)
;;(define-extern lowmemmap object) ;; unknown type
(define-extern kernel-write function)
(define-extern kernel-read function)
(define-extern deinstall-debug-handlers function)
(define-extern kernel-set-exception-vector function)
(define-extern kernel-copy-function function)
(define-extern kernel-check-hardwired-addresses function)
(define-extern kernel-set-level2-vector function)
(define-extern kernel-set-interrupt-vector function)
(define-extern kernel-read-function function)
(define-extern kernel-copy-to-kernel-ram function)
(define-extern kernel-write-function function)
(define-extern resend-exception function)
(define-extern deinstall-debug-handler function)
;;(define-extern install-debug-handler object) ;; unknown type
(define-extern copyn-string<-charp function)
(define-extern string<-charp function)
(define-extern string-get-arg!! function)
(define-extern string-get-float!! function)
(define-extern append-character-to-string function)
(define-extern string-get-int32!! function)
(define-extern charp-basename function)
(define-extern copy-charp<-charp function)
(define-extern string-skip-whitespace function)
(define-extern string-strip-whitespace! function)
(define-extern string<? function)
(define-extern string-get-flag!! function)
(define-extern charp<-string (function (pointer uint8) string int))
(define-extern string>=? function)
(define-extern string-charp= function)
(define-extern string->float function)
(define-extern cat-string<-string_to_charp function)
(define-extern string->int function)
(define-extern string<=? function)
(define-extern cat-string<-string function)
(define-extern clear function)
(define-extern string= function)
;;(define-extern *temp-string* object) ;; unknown type
(define-extern string-strip-leading-whitespace! function)
(define-extern catn-string<-charp function)
;;(define-extern *string-tmp-str* object) ;; unknown type
(define-extern string-suck-up! function)
(define-extern copy-string<-string (function string string symbol))
(define-extern string-skip-to-char function)
(define-extern string-strip-trailing-whitespace! function)
(define-extern string>? function)
(define-extern string-cat-to-last-char function)
;;(define-extern dgo-entry object) ;; unknown type
;;(define-extern dgo-file object) ;; unknown type
(define-extern enter-state function)
(define-extern send-event-function function)
(define-extern looping-code function)
(define-extern inherit-state function)
;;(define-extern time-frame object) ;; unknown type
;;(define-extern part-id object) ;; unknown type
@@ -53,10 +53,11 @@
"str_file_names_":[],
"type_hints_file":"decompiler/config/jak1_ntsc_black_label/type_hints.jsonc",
"anonymous_function_types_file":"decompiler/config/jak1_ntsc_black_label/anonymous_function_types.jsonc",
"analyze_functions":true,
"analyze_expressions":false,
"function_type_prop":false,
"function_type_prop":true,
"write_disassembly":true,
"write_hex_near_instructions":false,
@@ -92,6 +93,9 @@
"(method 3 vec4s)", // 128-bit bitfield
"qmem-copy<-!", // 128-bit loads and stores
"qmem-copy->!", // 128-bit loads and stores
"breakpoint-range-set!", // messing with COP0 registers
"(method 0 catch-frame)", // kernel asm
"throw-dispatch", // kernel asm
"reset-and-call", // stack manipulation
"(method 10 cpu-thread)" // loading saved regs off of the stack.
],
@@ -0,0 +1,10 @@
{
"gkernel":[
[16, "(function process symbol)"],
[22, "(function process symbol)"],
[25, "(function process symbol)"],
[28, "(function process symbol)"],
[30, "(function process symbol)"],
[32, "(function process symbol)"]
]
}
@@ -45,6 +45,21 @@
[13, ["v0", "cpu-thread"]]
],
"(method 0 dead-pool-heap)":[
[60, ["v0", "int"]], // a lie, actually the 115 is an align16 constant propagated on addr of heap start.
[61, ["a0", "pointer"], ["v0", "dead-pool-heap"]]
],
"(method 21 dead-pool-heap)":[
[5, ["v1", "pointer"]],
[13, ["a0", "pointer"]],
[25, ["v1", "pointer"]]
],
"(method 5 dead-pool-heap)":[
[3, ["v1", "int"], ["a0", "int"]]
],
"remove-exit":[
[0, ["s6", "process"]]
],
@@ -66,6 +81,32 @@
"(method 14 dead-pool)":[
[23, ["v1", "process"]], // bad visit order with #f?
[28, ["s4", "(pointer process-tree)"]] // bug in real game, see gkernel.gc
],
"throw":[
[20, ["s4", "protect-frame"]] // type case
],
"(method 0 protect-frame)":[
[0, ["a0", "int"]],
[1, ["v0", "protect-frame"]]
],
"(method 9 process)":[
[43, ["s5", "process"]]
],
"(method 10 process)":[
[24, ["s4", "protect-frame"]]
],
"enter-state":[
[67, ["s0", "protect-frame"]]
],
"name=":[
[24, ["a1", "symbol"]],
[39, ["a0", "symbol"]]
]
}
+4 -3
View File
@@ -58,7 +58,8 @@ void DecompilerTypeSystem::parse_type_defs(const std::vector<std::string>& file_
add_symbol(sym_name.as_symbol()->name, parse_typespec(&ts, sym_type));
} else if (car(o).as_symbol()->name == "deftype") {
parse_deftype(cdr(o), &ts);
auto dtr = parse_deftype(cdr(o), &ts);
add_symbol(dtr.type.base_type(), "type");
} else if (car(o).as_symbol()->name == "declare-type") {
auto* rest = &cdr(o);
auto type_name = car(*rest);
@@ -267,8 +268,8 @@ TP_Type DecompilerTypeSystem::tp_lca(const TP_Type& existing, const TP_Type& add
}
// otherwise, as an absolute fallback, convert both to TypeSpecs and do TypeSpec LCA
auto new_result =
TP_Type::make_from_typespec(ts.lowest_common_ancestor(existing.typespec(), add.typespec()));
auto new_result = TP_Type::make_from_typespec(
coerce_to_reg_type(ts.lowest_common_ancestor(existing.typespec(), add.typespec())));
*changed = (new_result != existing);
return new_result;
}
+1
View File
@@ -113,6 +113,7 @@ class TP_Type {
bool is_integer_constant() const { return kind == Kind::INTEGER_CONSTANT; }
bool is_integer_constant(int64_t value) const { return is_integer_constant() && m_int == value; }
bool is_product() const { return kind == Kind::PRODUCT_WITH_CONSTANT; }
bool is_product_plus_obj() const { return kind == Kind::OBJECT_PLUS_PRODUCT_WITH_CONSTANT; }
bool is_product_with(int64_t value) const {
return kind == Kind::PRODUCT_WITH_CONSTANT && m_int == value;
}
+1
View File
@@ -5,3 +5,4 @@
;; name in dgo: vu1-macros
;; dgos: GAME, ENGINE
;; this file has no code!
+12
View File
@@ -5,3 +5,15 @@
;; name in dgo: types-h
;; dgos: GAME, ENGINE
;; these types may have bitfields in them.
;; these have runtime types. But nothing appears to use them?
(deftype time-frame (int64)
()
:flag-assert #x900000008
)
(deftype part-id (uint32)
()
:flag-assert #x900000004
)
+9 -12
View File
@@ -232,7 +232,7 @@
(state state :offset-assert #x38)
(trans-hook function :offset-assert #x3c)
(post-hook function :offset-assert #x40)
(event-hook function :offset-assert #x44)
(event-hook (function stack-frame (function object) function state object) :offset-assert #x44)
(allocated-length int32 :offset-assert #x48)
(next-state basic :offset-assert #x4c)
(heap-base pointer :offset-assert #x50)
@@ -413,23 +413,20 @@
obj
)
;; GOAL State. A Process can be in a State.
(deftype state (protect-frame) ; state is a protect frame so we can "exit" it with cleanup
((code function :offset-assert 16) ;; main code to run in this state
(trans function :offset-assert 20) ;; ? run once and return ? when ?
(post function :offset-assert 24) ;; ?
(enter function :offset-assert 28) ;; ?
(event basic :offset-assert 32) ;; event handler function?
(deftype state (protect-frame)
((code function :offset-assert 16)
(trans (function object) :offset-assert 20)
(post function :offset-assert 24)
(enter (function object object object object object object object) :offset-assert 28)
(event basic :offset-assert 32)
)
(:methods
(new ((allocation symbol) (type-to-make type) (name basic) (code function)
(trans function) (enter function) (exit (function object)) (event function)) _type_ 0)
)
:size-assert #x24
:method-count-assert 9
:flag-assert #x900000024
:size-assert #x24
:flag-assert #x900000024
)
-5
View File
@@ -1286,8 +1286,6 @@
obj
)
(define-extern *stdcon* basic) ;; todo, more specific
(defmethod compact dead-pool-heap ((obj dead-pool-heap) (count int))
"Do heap compaction. The count argument tells us how much work to do.
If the heap is very full we will automatically do more work than requested."
@@ -1592,9 +1590,6 @@
(define-extern *listener-process* process)
(define-extern *active-pool* process-tree)
(define-extern *stdcon0* basic) ; more specific?
(define-extern *stdcon1* basic) ; more specific?
(define-extern *debug-draw-pauseable* symbol)
(defun kernel-dispatcher ()
"Run the kernel!
+6
View File
@@ -5,3 +5,9 @@
;; name in dgo: gstring-h
;; dgos: KERNEL
(define-extern *string-tmp-str* string)
(define-extern *temp-string* string)
(define-extern *stdcon0* string)
(define-extern *stdcon1* string)
(define-extern *stdcon* string)
(define-extern *debug-draw-pauseable* symbol)
+42 -1
View File
@@ -36,6 +36,7 @@
(&+! src-ptr 1)
)
)
dst
)
(defmethod new string ((allocation symbol) (type-to-make type) (size int) (other string))
@@ -59,6 +60,12 @@
)
)
;; string=
;; string-charp=
;; name=
;; copyn-string<-charp
;; string<-charp
(defun charp<-string ((dst (pointer uint8)) (src-string string))
"Copy a GOAL string into a character array."
(let ((src (-> src-string data)))
@@ -70,4 +77,38 @@
(set! (-> dst) 0)
0
)
)
)
;; copy-charp<-charp
;; cat-string<-string
;; catn-string<-charp
;; cat-string<-string_to_charp
;; append-character-to-string
;; charp-basename
;; clear
;; string<?
;; string>?
;; string<=?
;; string>=?
(define *string-tmp-str* (new 'global 'string 128 (the string #f)))
;; string-skip-to-char
;; string-cat-to-last-char
;; string-skip-whitespace
;; string-suck-up!
;; string-strip-leading-whitespace
;; string-strip-trailing-whitespace
;; string-strip-whitespace
;; string-get-arg!!
;; string->int
;; string->float
;; string-get-int32!!
;; string-get-float!!
;; string-get-flag!!
(define *debug-draw-pauseable* #f)
(define *stdcon0* (new 'global 'string 16384 (the string #f)))
(define *stdcon1* (new 'global 'string 16384 (the string #f)))
(define *stdcon* *stdcon0*)
(define *temp-string* (new 'global 'string 256 (the string #f)))
+125
View File
@@ -5,3 +5,128 @@
;; name in dgo: pskernel
;; dgos: KERNEL
#|
pskernel.gc contains a bunch of utilities for poking around the PS2's kernel.
These are unused in the retail game, and wouldn't be "safe" to use on a retail console,
as later console revisions may have different memory layout of the BIOS. I suspect this
was used for the purposes of debugging and handling crashes.
They took over syscall 102 to access memory inside the kernel.
There's an error message:
"I CANNOT CONTINUE TO LOAD GOAL, BECAUSE THE KERNEL VERSION HAS CHANGED~%"
"~%Alternatively, try removing pskernel.gc and debug-handlers.gc from project.cl, do an (lg) and then (:r) again~%"
My theory is that debug-handlers.gc held crash handlers, and on load would install them using functions defined in pskernel.gc.
In the retail game, they removed debug-handlers, but not pskernel.
In the PC port, all of these functions are just stubs.
|#
;; map of the kernel's memory.
(deftype lowmemmap (structure)
((irq-info-stack uint32 :offset-assert 0)
(irq2-info-stack uint32 :offset-assert 4)
(kernel-copy-fn uint32 :offset-assert 8)
(kernel-write-fn uint32 :offset-assert 12)
(r1-save uint128 :offset-assert 16)
(last-time uint32 :offset-assert 32)
(high-time uint32 :offset-assert 36)
(dma-status uint32 :offset-assert 40)
(dma-qnext uint32 :offset-assert 44)
(dma-qwc uint32 :offset-assert 48)
(dma-tnext uint32 :offset-assert 52)
(dma-stack0 uint32 :offset-assert 56)
(dma-stack1 uint32 :offset-assert 60)
(kernel-read-fn uint32 :offset-assert 64)
)
:method-count-assert 9
:size-assert #x44
:flag-assert #x900000044
)
(defmacro nyi-break (name)
`(begin
(format 0 "~A is not implemented!~%" (quote ,name))
(break)
(none)
)
)
(defun kernel-copy-function (unused source dest size)
"Copy size words from source to dest. The a0 argument is ignored.
Uses registers a0, a1, a2, a3"
(nyi-break kernel-copy-function)
)
(defun kernel-copy-to-kernel-ram ()
"Does a syscall 102. This is CpuConfig on released PS2 BIOSes.
I'm guessing this somehow calls kernel-copy-function"
(nyi-break kernel-copy-to-kernel-ram)
)
(defun kernel-write-function (unused source dest)
"Writes a single word to the destination"
(nyi-break kernel-write-function)
)
(defun kernel-write ()
"Does a syscall 102. I'm guessing this somehow calls kernel-write-function."
(nyi-break kernel-write)
)
(defun kernel-read-function (unused source)
"Read a single word (signed) from source"
(nyi-break kernel-read-function)
)
(defun kernel-read ()
"Does a syscall 102. I'm guessing this somehow calls kernel-read-function"
(nyi-break kernel-read)
)
(defun kernel-check-hardwired-addresses ()
"Checks a bunch of stuff in the kernel using kernel-read.
If the memory layout isn't what it expects, it prints an error and crashes."
(nyi-break kernel-check-hardwired-addresses)
)
(defun install-default-debug-handler (handler)
"Installs the given handler as the debug handler 1 through 13.
Uses the install-debug-handler function defined in kmachine.cpp"
(nyi-break install-default-debug-handler)
)
(defun return-from-exception (regs)
"Restore the registers and eret."
(nyi-break return-from-exception)
)
(defun kernel-set-exception-vector ()
"Use syscall 13/syscall 14 to set exception handlers"
(nyi-break kernel-set-exception-vector)
)
(defun kernel-set-interrupt-vector ()
"Use syscall 15 to set an interrupt handler"
(nyi-break kernel-set-interrupt-vector)
)
(defun kernel-set-level2-vector ()
"Set some handler by writing directly to kernel memory. Not sure what this is."
(nyi-break kernel-set-level2-vector)
)
(defun deinstall-debug-handler ()
"Set the kernel exception handler back to the default?"
(nyi-break deinstall-debug-handler)
)
(defun deinstall-debug-handlers ()
"Set a bunch of excpetion handlers back to the default?"
(nyi-break deinstall-debug-handlers)
)
(defun resend-exception ()
"I think this was to return from a GOAL crash handler back to the EE kernel's crash handler."
(nyi-break resend-exception)
)