|
|
|
@ -13,6 +13,7 @@
|
|
|
|
|
|
|
|
|
|
#include <alloy/runtime/debugger.h>
|
|
|
|
|
#include <xenia/emulator.h>
|
|
|
|
|
#include <xenia/export_resolver.h>
|
|
|
|
|
#include <xenia/cpu/xenon_memory.h>
|
|
|
|
|
#include <xenia/cpu/xenon_runtime.h>
|
|
|
|
|
#include <xenia/cpu/xex_module.h>
|
|
|
|
@ -236,8 +237,7 @@ json_t* Processor::OnDebugRequest(
|
|
|
|
|
if (xestrcmpa(command, "get_module_list") == 0) {
|
|
|
|
|
json_t* list = json_array();
|
|
|
|
|
Runtime::ModuleList modules = runtime_->GetModules();
|
|
|
|
|
for (Runtime::ModuleList::iterator it = modules.begin();
|
|
|
|
|
it != modules.end(); ++it) {
|
|
|
|
|
for (auto it = modules.begin(); it != modules.end(); ++it) {
|
|
|
|
|
XexModule* module = (XexModule*)(*it);
|
|
|
|
|
json_t* module_json = json_object();
|
|
|
|
|
json_t* module_name_json = json_string(module->name());
|
|
|
|
@ -246,7 +246,18 @@ json_t* Processor::OnDebugRequest(
|
|
|
|
|
}
|
|
|
|
|
return list;
|
|
|
|
|
} else if (xestrcmpa(command, "get_module") == 0) {
|
|
|
|
|
return json_null();
|
|
|
|
|
json_t* module_name_json = json_object_get(request, "module");
|
|
|
|
|
if (!module_name_json || !json_is_string(module_name_json)) {
|
|
|
|
|
succeeded = false;
|
|
|
|
|
return json_string("Module name not specified");
|
|
|
|
|
}
|
|
|
|
|
const char* module_name = json_string_value(module_name_json);
|
|
|
|
|
XexModule* module = (XexModule*)runtime_->GetModule(module_name);
|
|
|
|
|
if (!module) {
|
|
|
|
|
succeeded = false;
|
|
|
|
|
return json_string("Module not found");
|
|
|
|
|
}
|
|
|
|
|
return DumpModule(module, succeeded);
|
|
|
|
|
} else if (xestrcmpa(command, "get_function_list") == 0) {
|
|
|
|
|
json_t* module_name_json = json_object_get(request, "module");
|
|
|
|
|
if (!module_name_json || !json_is_string(module_name_json)) {
|
|
|
|
@ -285,63 +296,7 @@ json_t* Processor::OnDebugRequest(
|
|
|
|
|
return json_string("Function address not specified");
|
|
|
|
|
}
|
|
|
|
|
uint64_t address = (uint64_t)json_number_value(address_json);
|
|
|
|
|
|
|
|
|
|
FunctionInfo* info;
|
|
|
|
|
if (runtime_->LookupFunctionInfo(address, &info)) {
|
|
|
|
|
succeeded = false;
|
|
|
|
|
return json_string("Function not found");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Demand a new function with all debug info retained.
|
|
|
|
|
// If we ever wanted absolute x64 addresses/etc we could
|
|
|
|
|
// use the x64 from the function in the symbol table.
|
|
|
|
|
Function* fn;
|
|
|
|
|
if (runtime_->frontend()->DefineFunction(info, true, &fn)) {
|
|
|
|
|
succeeded = false;
|
|
|
|
|
return json_string("Unable to resolve function");
|
|
|
|
|
}
|
|
|
|
|
DebugInfo* debug_info = fn->debug_info();
|
|
|
|
|
if (!debug_info) {
|
|
|
|
|
succeeded = false;
|
|
|
|
|
return json_string("No debug info present for function");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_t* fn_json = json_object();
|
|
|
|
|
const char* name = info->name();
|
|
|
|
|
char name_buffer[32];
|
|
|
|
|
if (!name) {
|
|
|
|
|
xesnprintfa(name_buffer, XECOUNT(name_buffer), "sub_%.8X",
|
|
|
|
|
info->address());
|
|
|
|
|
name = name_buffer;
|
|
|
|
|
}
|
|
|
|
|
json_t* name_json = json_string(name);
|
|
|
|
|
json_object_set_new(fn_json, "name", name_json);
|
|
|
|
|
json_t* start_address_json = json_integer(info->address());
|
|
|
|
|
json_object_set_new(fn_json, "startAddress", start_address_json);
|
|
|
|
|
json_t* end_address_json = json_integer(info->end_address());
|
|
|
|
|
json_object_set_new(fn_json, "endAddress", end_address_json);
|
|
|
|
|
json_t* link_status_json = json_integer(info->status());
|
|
|
|
|
json_object_set_new(fn_json, "linkStatus", link_status_json);
|
|
|
|
|
|
|
|
|
|
json_t* disasm_json = json_object();
|
|
|
|
|
json_t* disasm_str_json;
|
|
|
|
|
disasm_str_json = json_loads(debug_info->source_json(), 0, NULL);
|
|
|
|
|
json_object_set_new(disasm_json, "source", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->raw_hir_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "rawHir", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->hir_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "hir", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->raw_lir_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "rawLir", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->lir_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "lir", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->machine_code_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "machineCode", disasm_str_json);
|
|
|
|
|
json_object_set_new(fn_json, "disasm", disasm_json);
|
|
|
|
|
|
|
|
|
|
delete fn;
|
|
|
|
|
|
|
|
|
|
return fn_json;
|
|
|
|
|
return DumpFunction(address, succeeded);
|
|
|
|
|
} else if (xestrcmpa(command, "add_breakpoints") == 0) {
|
|
|
|
|
// breakpoints: [{}]
|
|
|
|
|
json_t* breakpoints_json = json_object_get(request, "breakpoints");
|
|
|
|
@ -442,6 +397,303 @@ json_t* Processor::OnDebugRequest(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_t* json_object_set_string_new(
|
|
|
|
|
json_t* object, const char* key, const char* value) {
|
|
|
|
|
json_t* value_json = json_string(value);
|
|
|
|
|
json_object_set_new(object, key, value_json);
|
|
|
|
|
return value_json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_t* json_object_set_string_format_new(
|
|
|
|
|
json_t* object, const char* key, const char* format, ...) {
|
|
|
|
|
char buffer[1024];
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, format);
|
|
|
|
|
xevsnprintfa(buffer, XECOUNT(buffer), format, args);
|
|
|
|
|
va_end(args);
|
|
|
|
|
json_t* value_json = json_string(buffer);
|
|
|
|
|
json_object_set_new(object, key, value_json);
|
|
|
|
|
return value_json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_t* json_object_set_integer_new(
|
|
|
|
|
json_t* object, const char* key, json_int_t value) {
|
|
|
|
|
json_t* value_json = json_integer(value);
|
|
|
|
|
json_object_set_new(object, key, value_json);
|
|
|
|
|
return value_json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_t* Processor::DumpModule(XexModule* module, bool& succeeded) {
|
|
|
|
|
auto xex = module->xex();
|
|
|
|
|
auto header = xe_xex2_get_header(xex);
|
|
|
|
|
|
|
|
|
|
auto export_resolver = runtime_->export_resolver();
|
|
|
|
|
|
|
|
|
|
json_t* module_json = json_object();
|
|
|
|
|
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
module_json, "moduleFlags", header->module_flags);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
module_json, "systemFlags", header->system_flags);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
module_json, "exeAddress", header->exe_address);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
module_json, "exeEntryPoint", header->exe_entry_point);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
module_json, "exeStackSize", header->exe_stack_size);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
module_json, "exeHeapSize", header->exe_heap_size);
|
|
|
|
|
|
|
|
|
|
json_t* exec_info_json = json_object();
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
exec_info_json, "mediaId", header->execution_info.media_id);
|
|
|
|
|
json_object_set_string_format_new(
|
|
|
|
|
exec_info_json, "version", "%d.%d.%d.%d",
|
|
|
|
|
header->execution_info.version.major,
|
|
|
|
|
header->execution_info.version.minor,
|
|
|
|
|
header->execution_info.version.build,
|
|
|
|
|
header->execution_info.version.qfe);
|
|
|
|
|
json_object_set_string_format_new(
|
|
|
|
|
exec_info_json, "baseVersion", "%d.%d.%d.%d",
|
|
|
|
|
header->execution_info.base_version.major,
|
|
|
|
|
header->execution_info.base_version.minor,
|
|
|
|
|
header->execution_info.base_version.build,
|
|
|
|
|
header->execution_info.base_version.qfe);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
exec_info_json, "titleId", header->execution_info.title_id);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
exec_info_json, "platform", header->execution_info.platform);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
exec_info_json, "executableTable", header->execution_info.executable_table);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
exec_info_json, "discNumber", header->execution_info.disc_number);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
exec_info_json, "discCount", header->execution_info.disc_count);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
exec_info_json, "savegameId", header->execution_info.savegame_id);
|
|
|
|
|
json_object_set_new(module_json, "executionInfo", exec_info_json);
|
|
|
|
|
|
|
|
|
|
json_t* loader_info_json = json_object();
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
loader_info_json, "imageFlags", header->loader_info.image_flags);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
loader_info_json, "gameRegions", header->loader_info.game_regions);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
loader_info_json, "mediaFlags", header->loader_info.media_flags);
|
|
|
|
|
json_object_set_new(module_json, "loaderInfo", loader_info_json);
|
|
|
|
|
|
|
|
|
|
json_t* tls_info_json = json_object();
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
tls_info_json, "slotCount", header->tls_info.slot_count);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
tls_info_json, "dataSize", header->tls_info.data_size);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
tls_info_json, "rawDataAddress", header->tls_info.raw_data_address);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
tls_info_json, "rawDataSize", header->tls_info.raw_data_size);
|
|
|
|
|
json_object_set_new(module_json, "tlsInfo", tls_info_json);
|
|
|
|
|
|
|
|
|
|
json_t* headers_json = json_array();
|
|
|
|
|
for (size_t n = 0; n < header->header_count; n++) {
|
|
|
|
|
auto opt_header = &header->headers[n];
|
|
|
|
|
json_t* header_entry_json = json_object();
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
header_entry_json, "key", opt_header->key);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
header_entry_json, "offset", opt_header->offset);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
header_entry_json, "length", opt_header->length);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
header_entry_json, "value", opt_header->value);
|
|
|
|
|
json_array_append_new(headers_json, header_entry_json);
|
|
|
|
|
}
|
|
|
|
|
json_object_set_new(module_json, "headers", headers_json);
|
|
|
|
|
|
|
|
|
|
// TODO(benvanik): resources.
|
|
|
|
|
json_t* resource_info_json = json_object();
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
resource_info_json, "address", header->resource_info.address);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
resource_info_json, "size", header->resource_info.size);
|
|
|
|
|
json_object_set_new(module_json, "resourceInfo", resource_info_json);
|
|
|
|
|
|
|
|
|
|
json_t* sections_json = json_array();
|
|
|
|
|
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
|
|
|
|
const xe_xex2_section_t* section = &header->sections[n];
|
|
|
|
|
const char* type = "unknown";
|
|
|
|
|
switch (section->info.type) {
|
|
|
|
|
case XEX_SECTION_CODE:
|
|
|
|
|
type = "code";
|
|
|
|
|
break;
|
|
|
|
|
case XEX_SECTION_DATA:
|
|
|
|
|
type = "rwdata";
|
|
|
|
|
break;
|
|
|
|
|
case XEX_SECTION_READONLY_DATA:
|
|
|
|
|
type = "rodata";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
const size_t start_address =
|
|
|
|
|
header->exe_address + (i * xe_xex2_section_length);
|
|
|
|
|
const size_t end_address =
|
|
|
|
|
start_address + (section->info.page_count * xe_xex2_section_length);
|
|
|
|
|
json_t* section_entry_json = json_object();
|
|
|
|
|
json_object_set_string_new(
|
|
|
|
|
section_entry_json, "type", type);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
section_entry_json, "pageCount", section->info.page_count);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
section_entry_json, "startAddress", start_address);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
section_entry_json, "endAddress", end_address);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
section_entry_json, "totalLength",
|
|
|
|
|
section->info.page_count * xe_xex2_section_length);
|
|
|
|
|
json_array_append_new(sections_json, section_entry_json);
|
|
|
|
|
i += section->info.page_count;
|
|
|
|
|
}
|
|
|
|
|
json_object_set_new(module_json, "sections", sections_json);
|
|
|
|
|
|
|
|
|
|
json_t* static_libraries_json = json_array();
|
|
|
|
|
for (size_t n = 0; n < header->static_library_count; n++) {
|
|
|
|
|
const xe_xex2_static_library_t *library = &header->static_libraries[n];
|
|
|
|
|
json_t* static_library_entry_json = json_object();
|
|
|
|
|
json_object_set_string_new(
|
|
|
|
|
static_library_entry_json, "name", library->name);
|
|
|
|
|
json_object_set_string_format_new(
|
|
|
|
|
static_library_entry_json, "version", "%d.%d.%d.%d",
|
|
|
|
|
library->major, library->minor, library->build, library->qfe);
|
|
|
|
|
json_array_append_new(static_libraries_json, static_library_entry_json);
|
|
|
|
|
}
|
|
|
|
|
json_object_set_new(module_json, "staticLibraries", static_libraries_json);
|
|
|
|
|
|
|
|
|
|
json_t* library_imports_json = json_array();
|
|
|
|
|
for (size_t n = 0; n < header->import_library_count; n++) {
|
|
|
|
|
const xe_xex2_import_library_t* library = &header->import_libraries[n];
|
|
|
|
|
xe_xex2_import_info_t* import_infos;
|
|
|
|
|
size_t import_info_count;
|
|
|
|
|
if (xe_xex2_get_import_infos(
|
|
|
|
|
xex, library, &import_infos, &import_info_count)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_t* import_library_json = json_object();
|
|
|
|
|
json_object_set_string_new(
|
|
|
|
|
import_library_json, "name", library->name);
|
|
|
|
|
json_object_set_string_format_new(
|
|
|
|
|
import_library_json, "version", "%d.%d.%d.%d",
|
|
|
|
|
library->version.major, library->version.minor,
|
|
|
|
|
library->version.build, library->version.qfe);
|
|
|
|
|
json_object_set_string_format_new(
|
|
|
|
|
import_library_json, "minVersion", "%d.%d.%d.%d",
|
|
|
|
|
library->min_version.major, library->min_version.minor,
|
|
|
|
|
library->min_version.build, library->min_version.qfe);
|
|
|
|
|
|
|
|
|
|
json_t* imports_json = json_array();
|
|
|
|
|
for (size_t m = 0; m < import_info_count; m++) {
|
|
|
|
|
auto info = &import_infos[m];
|
|
|
|
|
auto kernel_export = export_resolver->GetExportByOrdinal(
|
|
|
|
|
library->name, info->ordinal);
|
|
|
|
|
json_t* import_json = json_object();
|
|
|
|
|
if (kernel_export) {
|
|
|
|
|
json_object_set_string_new(
|
|
|
|
|
import_json, "name", kernel_export->name);
|
|
|
|
|
json_object_set_new(
|
|
|
|
|
import_json, "implemented",
|
|
|
|
|
kernel_export->is_implemented ? json_true() : json_false());
|
|
|
|
|
} else {
|
|
|
|
|
json_object_set_new(import_json, "name", json_null());
|
|
|
|
|
json_object_set_new(import_json, "implemented", json_false());
|
|
|
|
|
}
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
import_json, "ordinal", info->ordinal);
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
import_json, "valueAddress", info->value_address);
|
|
|
|
|
if (kernel_export && kernel_export->type == KernelExport::Variable) {
|
|
|
|
|
json_object_set_string_new(
|
|
|
|
|
import_json, "type", "function");
|
|
|
|
|
} else if (kernel_export) {
|
|
|
|
|
json_object_set_string_new(
|
|
|
|
|
import_json, "type", "function");
|
|
|
|
|
json_object_set_integer_new(
|
|
|
|
|
import_json, "thunkAddress", info->thunk_address);
|
|
|
|
|
} else {
|
|
|
|
|
json_object_set_string_new(
|
|
|
|
|
import_json, "type", "unknown");
|
|
|
|
|
}
|
|
|
|
|
json_array_append_new(imports_json, import_json);
|
|
|
|
|
}
|
|
|
|
|
json_object_set_new(import_library_json, "imports", imports_json);
|
|
|
|
|
|
|
|
|
|
json_array_append_new(library_imports_json, import_library_json);
|
|
|
|
|
xe_free(import_infos);
|
|
|
|
|
}
|
|
|
|
|
json_object_set_new(module_json, "libraryImports", library_imports_json);
|
|
|
|
|
|
|
|
|
|
// TODO(benvanik): exports?
|
|
|
|
|
|
|
|
|
|
return module_json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_t* Processor::DumpFunction(uint64_t address, bool& succeeded) {
|
|
|
|
|
FunctionInfo* info;
|
|
|
|
|
if (runtime_->LookupFunctionInfo(address, &info)) {
|
|
|
|
|
succeeded = false;
|
|
|
|
|
return json_string("Function not found");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Demand a new function with all debug info retained.
|
|
|
|
|
// If we ever wanted absolute x64 addresses/etc we could
|
|
|
|
|
// use the x64 from the function in the symbol table.
|
|
|
|
|
Function* fn;
|
|
|
|
|
if (runtime_->frontend()->DefineFunction(info, true, &fn)) {
|
|
|
|
|
succeeded = false;
|
|
|
|
|
return json_string("Unable to resolve function");
|
|
|
|
|
}
|
|
|
|
|
DebugInfo* debug_info = fn->debug_info();
|
|
|
|
|
if (!debug_info) {
|
|
|
|
|
succeeded = false;
|
|
|
|
|
return json_string("No debug info present for function");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
json_t* fn_json = json_object();
|
|
|
|
|
const char* name = info->name();
|
|
|
|
|
char name_buffer[32];
|
|
|
|
|
if (!name) {
|
|
|
|
|
xesnprintfa(name_buffer, XECOUNT(name_buffer), "sub_%.8X",
|
|
|
|
|
info->address());
|
|
|
|
|
name = name_buffer;
|
|
|
|
|
}
|
|
|
|
|
json_t* name_json = json_string(name);
|
|
|
|
|
json_object_set_new(fn_json, "name", name_json);
|
|
|
|
|
json_t* start_address_json = json_integer(info->address());
|
|
|
|
|
json_object_set_new(fn_json, "startAddress", start_address_json);
|
|
|
|
|
json_t* end_address_json = json_integer(info->end_address());
|
|
|
|
|
json_object_set_new(fn_json, "endAddress", end_address_json);
|
|
|
|
|
json_t* link_status_json = json_integer(info->status());
|
|
|
|
|
json_object_set_new(fn_json, "linkStatus", link_status_json);
|
|
|
|
|
|
|
|
|
|
json_t* disasm_json = json_object();
|
|
|
|
|
json_t* disasm_str_json;
|
|
|
|
|
disasm_str_json = json_loads(debug_info->source_json(), 0, NULL);
|
|
|
|
|
json_object_set_new(disasm_json, "source", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->raw_hir_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "rawHir", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->hir_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "hir", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->raw_lir_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "rawLir", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->lir_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "lir", disasm_str_json);
|
|
|
|
|
disasm_str_json = json_string(debug_info->machine_code_disasm());
|
|
|
|
|
json_object_set_new(disasm_json, "machineCode", disasm_str_json);
|
|
|
|
|
json_object_set_new(fn_json, "disasm", disasm_json);
|
|
|
|
|
|
|
|
|
|
delete fn;
|
|
|
|
|
|
|
|
|
|
succeeded = true;
|
|
|
|
|
return fn_json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Processor::DebugClientState::DebugClientState(XenonRuntime* runtime) :
|
|
|
|
|
runtime_(runtime) {
|
|
|
|
|
breakpoints_lock_ = xe_mutex_alloc(10000);
|
|
|
|
|