// SPDX-FileCopyrightText: 2022 GustavoLCR <gugulcr@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only

#include <rz_core.h>
#include <rz_debug.h>
#include <dmp_specs.h>
#include <dmp64.h>
#include <pe_specs.h>
#include <winkd.h>
#include "common_winkd.h"

#include "native/bt/windows-x64.c"
#include "native/bt/generic-all.c"

static bool rz_debug_dmp_init(RzDebug *dbg, void **user) {
	RzCore *core = dbg->corebind.core;
	RzIODesc *desc = core->io->desc;
	if (!desc) {
		return false;
	}
	if (strcmp(desc->plugin->name, "dmp")) {
		RZ_LOG_ERROR("Open a file with dmp:// to use the 'dmp' debug plugin\n");
		return false;
	}
	RzBinInfo *info = core->bin->cur->o->info;
	if (!info || !info->rclass || strcmp(info->rclass, "dmp64")) {
		RZ_LOG_ERROR("Open a Windows kernel dump file with dmp:// to use the 'dmp' debug plugin\n");
		return false;
	}

	dbg->plugin_data = core->io->desc->data;
	DmpCtx *ctx = dbg->plugin_data;
	ctx->bf = core->bin->cur;

	int ret = rz_hex_str2bin(core->bin->cur->o->regstate, NULL);
	ctx->context = malloc(ret);
	if (!ctx->context) {
		return false;
	}
	ctx->context_sz = ret;
	rz_hex_str2bin(core->bin->cur->o->regstate, ctx->context);

	ut32 MachineImageType = 0; // Windows Architecture (IMAGE_FILE_MACHINE)
	ut32 MinorVersion = 0; // Windows Version
	ut32 ServicePackBuild = 0;
	ut32 ProcessOffset = 0;
	ut32 ThreadOffset = 0;
	ut32 CallStackOffset = 0;
	ut32 SizeOfCallStack = 0;
	ut64 TopOfStack = 0;
	ut32 NumberProcessors = 0;
	ctx->target = TARGET_BACKEND;
	dbg->corebind.cmd(dbg->corebind.core, "e io.va=0");
	RzBuffer *b = rz_buf_new_with_io(&dbg->iob);
	rz_buf_read_le64_at(b, rz_offsetof(dmp64_header, DirectoryTableBase), &ctx->kernelDirectoryTable);
	rz_buf_read_le64_at(b, rz_offsetof(dmp64_header, PsActiveProcessHead), &ctx->windctx.PsActiveProcessHead);
	rz_buf_read_le64_at(b, rz_offsetof(dmp64_header, PsLoadedModuleList), &ctx->windctx.PsLoadedModuleList);
	rz_buf_read_le64_at(b, rz_offsetof(dmp64_header, KdDebuggerDataBlock), &ctx->windctx.KdDebuggerDataBlock);
	rz_buf_read_le32_at(b, rz_offsetof(dmp64_header, NumberProcessors), &NumberProcessors);
	rz_buf_read_le32_at(b, rz_offsetof(dmp64_header, DumpType), &ctx->type);
	rz_buf_read_le32_at(b, rz_offsetof(dmp64_header, MachineImageType), &MachineImageType);
	rz_buf_read_le32_at(b, rz_offsetof(dmp64_header, MinorVersion), &MinorVersion);
	rz_buf_read_le32_at(b, sizeof(dmp64_header) + rz_offsetof(dmp64_triage, ServicePackBuild), &ServicePackBuild);
	rz_buf_read_le32_at(b, sizeof(dmp64_header) + rz_offsetof(dmp64_triage, ProcessOffset), &ProcessOffset);
	rz_buf_read_le32_at(b, sizeof(dmp64_header) + rz_offsetof(dmp64_triage, ThreadOffset), &ThreadOffset);
	rz_buf_read_le32_at(b, sizeof(dmp64_header) + rz_offsetof(dmp64_triage, CallStackOffset), &CallStackOffset);
	rz_buf_read_le32_at(b, sizeof(dmp64_header) + rz_offsetof(dmp64_triage, SizeOfCallStack), &SizeOfCallStack);
	rz_buf_read_le64_at(b, sizeof(dmp64_header) + rz_offsetof(dmp64_triage, TopOfStack), &TopOfStack);
	rz_buf_free(b);

	RzIOMap *map = rz_io_map_get(core->io, 0);
	if (map) {
		// Remove file mapping
		rz_io_map_del(core->io, map->id);
	}
	if (ctx->type == DMP_DUMPTYPE_TRIAGE) {
		dbg->corebind.cfgSetI(dbg->corebind.core, "io.va", 1);
		ctx->target = TARGET_BACKEND;
		ctx->kernelDirectoryTable = TARGET_BACKEND;
	} else {
		ctx->target = TARGET_KERNEL;
		ServicePackBuild = winkd_get_sp(&ctx->windctx);
	}

	switch (MachineImageType) {
	case PE_IMAGE_FILE_MACHINE_ARM64:
		ctx->windctx.is_arm = true;
		ctx->windctx.is_64bit = true;
		ctx->windctx.is_pae = true;
		break;
	case PE_IMAGE_FILE_MACHINE_ARMNT:
		ctx->windctx.is_arm = true;
		ctx->windctx.is_pae = true;
		break;
	case PE_IMAGE_FILE_MACHINE_AMD64:
		ctx->windctx.is_64bit = true;
		ctx->windctx.is_pae = true;
		break;
	default:
		return false;
	}

	ctx->windctx.profile = winkd_get_profile(dbg->bits * 8, MinorVersion, ServicePackBuild);

	// Find ntoskrnl.exe module
	RzListIter *it;
	WindModule mod = { 0 };
	if (ctx->type == DMP_DUMPTYPE_TRIAGE) {
		struct rz_bin_dmp64_obj_t *obj = core->bin->cur->o->bin_obj;
		dmp_driver_desc *driver;
		rz_list_foreach (obj->drivers, it, driver) {
			if (rz_str_endswith(driver->file, "\\ntoskrnl.exe")) {
				mod.name = driver->file;
				mod.addr = driver->base;
				mod.size = driver->size;
				mod.timestamp = driver->timestamp;
				break;
			}
		}
	} else {
		WindProc kernel = { .dir_base_table = ctx->kernelDirectoryTable, .uniqueid = 4 };
		ctx->windctx.target = kernel;
		RzList *modules = winkd_list_modules(&ctx->windctx);
		WindModule *m;
		rz_list_foreach (modules, it, m) {
			if (rz_str_endswith(m->name, "\\ntoskrnl.exe")) {
				mod = *m;
				break;
			}
		}
		ctx->windctx.target.uniqueid = 0;
	}
	char *kernel_pdb = NULL;
	if (mod.name) {
		core->bin->cur->o->opts.baseaddr = mod.addr;
		const char *server = dbg->corebind.cfgGet(dbg->corebind.core, "pdb.server");
		const char *symstore = dbg->corebind.cfgGet(dbg->corebind.core, "pdb.symstore");
		char *pdbpath, *exepath;
		if (winkd_download_module_and_pdb(&mod, server, symstore, &exepath, &pdbpath)) {
			// TODO: Convert to API call
			dbg->corebind.cmdf(dbg->corebind.core, "idp \"%s\"", pdbpath);
			free(exepath);
			kernel_pdb = strdup(rz_file_basename(pdbpath));
			free(pdbpath);
			if (!ctx->windctx.profile) {
				winkd_build_profile(&ctx->windctx, dbg->analysis->typedb);
				if (ctx->windctx.profile) {
					ctx->windctx.profile->build = MinorVersion;
					ctx->windctx.profile->sp = ServicePackBuild;
				}
			}
		} else {
			RZ_LOG_WARN("Failed to download ntoskrnl.pdb, many things won't work.\n");
		}
	}

	if (!ctx->windctx.profile) {
		RZ_LOG_ERROR("Could not find a profile for this Windows: %s %" PFMT32d "-bit %" PFMT32u " SP %" PFMT32u "\n",
			ctx->windctx.is_arm ? "ARM" : "x86", dbg->bits * 8, MinorVersion, ServicePackBuild);
		return false;
	}

	ctx->kthread_process_offset = rz_type_db_struct_member_offset(dbg->analysis->typedb, "_KTHREAD", "Process");
	ctx->kprcb_context_offset = rz_type_db_struct_member_offset(dbg->analysis->typedb, "_KPRCB", "Context");
	if (ctx->windctx.is_arm) {
		const ut64 switch_frame_offset = rz_type_db_struct_member_offset(dbg->analysis->typedb, "_KTHREAD", "SwitchFrame");
		ctx->kthread_switch_frame_offset = switch_frame_offset + rz_type_db_struct_member_offset(dbg->analysis->typedb, "_KSWITCH_FRAME", "Fp");
	}
	char *kpb_flag_name;
	if (kernel_pdb) {
		rz_str_replace(kernel_pdb, ".pdb", "", 0);
		kpb_flag_name = rz_str_newf("pdb.%s.KiProcessorBlock", kernel_pdb);
		free(kernel_pdb);
	} else {
		kpb_flag_name = strdup("0");
	}
	const ut64 KiProcessorBlock = dbg->corebind.numGet(dbg->corebind.core, kpb_flag_name);
	free(kpb_flag_name);
	ut64 i;
	for (i = 0; i < NumberProcessors; i++) {
		ut64 address = KiProcessorBlock + i * (ctx->windctx.is_64bit ? 8 : 4);
		ut64 kprcb = winkd_read_ptr_at(&ctx->windctx, ctx->windctx.read_at_kernel_virtual, address);
		rz_vector_push(&ctx->KiProcessorBlock, &kprcb);
	}

	if (ctx->type == DMP_DUMPTYPE_TRIAGE) {
		// Map Call stack into address space
		map = rz_io_map_new(core->io, desc->fd, RZ_PERM_R, CallStackOffset, TopOfStack, SizeOfCallStack);

		// Map ETHREAD into address space
		const ut64 address = 0x1000;
		map = rz_io_map_new(core->io, desc->fd, RZ_PERM_R, ThreadOffset, address, CallStackOffset - ThreadOffset);
		map->name = strdup("kernel.target.ethread");
		WindThread *target_thread = winkd_get_thread_at(&ctx->windctx, address);

		ctx->windctx.target_thread.ethread = address;
		const ut64 current_thread_offset = ctx->windctx.is_64bit ? 8 : 4;
		ut64 *kprcb;
		rz_vector_foreach(&ctx->KiProcessorBlock, kprcb) {
			const ut64 current_thread = winkd_read_ptr_at(&ctx->windctx, ctx->windctx.read_at_kernel_virtual, *kprcb + current_thread_offset);
			WindThread *thread = winkd_get_thread_at(&ctx->windctx, current_thread);
			if (thread && thread->uniqueid == target_thread->uniqueid) {
				// Map EPROCESS into address space
				const ut64 current_process = winkd_read_ptr_at(&ctx->windctx, ctx->windctx.read_at_kernel_virtual, thread->ethread + ctx->kthread_process_offset);
				RzIOMap *map = rz_io_map_new(core->io, desc->fd, RZ_PERM_R, ProcessOffset, current_process, ThreadOffset - ProcessOffset);
				map->name = strdup("kernel.target.eprocess");
				WindProc *process = winkd_get_process_at(&ctx->windctx, current_process);
				ctx->windctx.target = *process;
				ctx->windctx.target_thread = *thread;
				free(process);
				free(thread);
				break;
			}
			free(thread);
		}
		rz_io_map_remap(core->io, map->id, ctx->windctx.target_thread.ethread);
		free(target_thread);
	}

	return true;
}

static int rz_debug_dmp_attach(RzDebug *dbg, int pid) {
	DmpCtx *ctx = dbg->plugin_data;
	if (ctx->type == DMP_DUMPTYPE_TRIAGE) {
		dbg->pid = ctx->windctx.target.uniqueid;
		dbg->tid = ctx->windctx.target_thread.uniqueid;
		return dbg->pid;
	}
	const ut64 current_thread_offset = ctx->windctx.is_64bit ? 8 : 4;
	ut64 *kprcb;
	rz_vector_foreach_prev(&ctx->KiProcessorBlock, kprcb) {
		const ut64 current_thread = winkd_read_ptr_at(&ctx->windctx, ctx->windctx.read_at_kernel_virtual, *kprcb + current_thread_offset);
		WindThread *thread = winkd_get_thread_at(&ctx->windctx, current_thread);
		if (!thread) {
			continue;
		}
		const ut64 current_process = winkd_read_ptr_at(&ctx->windctx, ctx->windctx.read_at_kernel_virtual, thread->ethread + ctx->kthread_process_offset);
		WindProc *process = winkd_get_process_at(&ctx->windctx, current_process);
		if (!process || (!process->uniqueid && !strncmp(process->name, "Idle", sizeof(process->name)))) {
			free(thread);
			free(process);
			continue;
		}
		ctx->windctx.target = *process;
		ctx->windctx.target_thread = *thread;
		free(thread);
		free(process);
		break;
	}

	dbg->pid = ctx->windctx.target.uniqueid;
	dbg->tid = ctx->windctx.target_thread.uniqueid;
	return ctx->windctx.target.uniqueid;
}

static RzList *rz_debug_dmp_pids(RzDebug *dbg, int pid) {
	DmpCtx *ctx = dbg->plugin_data;
	RzList *ret = rz_list_newf((RzListFree)rz_debug_pid_free);
	if (!ret) {
		return NULL;
	}
	RzVector procs;
	rz_vector_init(&procs, sizeof(ut64), NULL, NULL);
	const ut64 current_thread_offset = ctx->windctx.is_64bit ? 8 : 4;
	ut64 *kprcb;
	// Get currently running processes
	rz_vector_foreach_prev(&ctx->KiProcessorBlock, kprcb) {
		const ut64 current_thread = winkd_read_ptr_at(&ctx->windctx, ctx->windctx.read_at_kernel_virtual, *kprcb + current_thread_offset);
		ut64 current_process = winkd_read_ptr_at(&ctx->windctx, ctx->windctx.read_at_kernel_virtual, current_thread + ctx->kthread_process_offset);
		rz_vector_push(&procs, &current_process);
	}

	RzList *pids = winkd_list_process(&ctx->windctx);
	RzListIter *it;
	WindProc *p;
	rz_list_foreach (pids, it, p) {
		RzDebugPid *newpid = RZ_NEW0(RzDebugPid);
		if (!newpid) {
			rz_vector_fini(&procs);
			rz_list_free(ret);
			rz_list_free(pids);
			return NULL;
		}
		newpid->path = strdup(p->name);
		newpid->pid = p->uniqueid;
		newpid->status = 's';
		newpid->runnable = true;
		ut64 *process;
		rz_vector_foreach(&procs, process) {
			if (*process == p->eprocess) {
				newpid->status = 'r';
			}
		}
		rz_list_append(ret, newpid);
	}
	rz_vector_fini(&procs);
	rz_list_free(pids);
	return ret;
}

static int rz_debug_dmp_select(RzDebug *dbg, int pid, int tid) {
	DmpCtx *ctx = dbg->plugin_data;
	if (ctx->type == DMP_DUMPTYPE_TRIAGE) {
		if (pid != ctx->windctx.target.uniqueid || tid != ctx->windctx.target_thread.uniqueid) {
			RZ_LOG_ERROR("Cannot select other targets on a triage dump\n");
		}
		dbg->pid = ctx->windctx.target.uniqueid;
		dbg->tid = ctx->windctx.target_thread.uniqueid;
		return ctx->windctx.target_thread.uniqueid;
	}

	if (winkd_set_target(&ctx->windctx, pid, tid)) {
		ctx->target = TARGET_VIRTUAL;
	} else {
		ctx->target = TARGET_PHYSICAL;
	}
	dbg->pid = ctx->windctx.target.uniqueid;
	dbg->tid = ctx->windctx.target_thread.uniqueid;
	return ctx->windctx.target_thread.uniqueid;
}

static inline bool is_kernel_address_present(WindCtx *ctx, ut64 at) {
	ut8 ptr_buf[8];
	if (!ctx->read_at_kernel_virtual(ctx->user, at, ptr_buf, ctx->is_64bit ? 8 : 4)) {
		return false;
	}
	return true;
}

static int rz_debug_dmp_reg_read(RzDebug *dbg, int type, ut8 *buf, int size) {
	DmpCtx *dmp = dbg->plugin_data;
	WindCtx *ctx = &dmp->windctx;
	if (!is_kernel_address_present(ctx, ctx->target_thread.ethread)) {
		return 0;
	}
	const ut64 current_thread_offset = ctx->is_64bit ? 8 : 4;
	ut64 *kprcb;
	rz_vector_foreach(&dmp->KiProcessorBlock, kprcb) {
		const ut64 current_thread = winkd_read_ptr_at(ctx, ctx->read_at_kernel_virtual, *kprcb + current_thread_offset);
		if (current_thread == ctx->target_thread.ethread) {
			const ut64 current_context = winkd_read_ptr_at(ctx, ctx->read_at_kernel_virtual, *kprcb + dmp->kprcb_context_offset);
			if (current_context) {
				return ctx->read_at_kernel_virtual(ctx->user, current_context, buf, size);
			}
			RZ_LOG_WARN("Failed to get KPRCB Context pointer at 0x%" PFMT64x "\n", current_context);
		}
	}
	if (dmp->type == DMP_DUMPTYPE_TRIAGE || !ctx->target_thread.uniqueid) {
		memcpy(buf, dmp->context, RZ_MIN(size, dmp->context_sz));
		return size;
	}
	const int kernel_stack_offset = ctx->is_64bit ? 0x58 : 0x48;
	if (ctx->is_arm) {
		if (ctx->is_64bit) {
			struct context_type_arm64 *regs = (struct context_type_arm64 *)buf;
			regs->Sp = winkd_read_ptr_at(ctx, ctx->read_at_kernel_virtual, ctx->target_thread.ethread + kernel_stack_offset);
			regs->Fp = winkd_read_ptr_at(ctx, ctx->read_at_kernel_virtual, ctx->target_thread.ethread + dmp->kthread_switch_frame_offset);
			regs->Pc = winkd_read_ptr_at(ctx, ctx->read_at_kernel_virtual, ctx->target_thread.ethread + dmp->kthread_switch_frame_offset + 8);
		} else {
			struct context_type_arm *regs = (struct context_type_arm *)buf;
			regs->sp = winkd_read_ptr_at(ctx, ctx->read_at_kernel_virtual, ctx->target_thread.ethread + kernel_stack_offset);
			regs->pc = winkd_read_ptr_at(ctx, ctx->read_at_kernel_virtual, ctx->target_thread.ethread + dmp->kthread_switch_frame_offset + 4);
		}
	} else {
		if (ctx->is_64bit) {
			struct context_type_amd64 *regs = (struct context_type_amd64 *)buf;
			regs->rsp = winkd_read_ptr_at(ctx, ctx->read_at_kernel_virtual, ctx->target_thread.ethread + kernel_stack_offset);
		} else {
			struct context_type_i386 *regs = (struct context_type_i386 *)buf;
			regs->esp = winkd_read_ptr_at(ctx, ctx->read_at_kernel_virtual, ctx->target_thread.ethread + kernel_stack_offset);
		}
	}
	return size;
}

static char *rz_debug_dmp_reg_profile(RzDebug *dbg) {
	DmpCtx *ctx = dbg->plugin_data;
	if (!ctx) {
#include "native/reg/windows-x64.h"
	}
	if (ctx->windctx.is_arm) {
		if (ctx->windctx.is_64bit) {
#include "native/reg/windows-arm64.h"
		}
#include "native/reg/windows-arm.h"
	}
	if (ctx->windctx.is_64bit) {
#include "native/reg/windows-x64.h"
	}
#include "native/reg/windows-x86.h"
	return NULL;
}

static RzList *rz_debug_dmp_threads(RzDebug *dbg, int pid) {
	DmpCtx *ctx = dbg->plugin_data;
	RzList *ret = rz_list_newf(free);
	if (!ret) {
		return NULL;
	}
	RzList *threads = winkd_list_threads(&ctx->windctx);
	RzListIter *it;
	WindThread *t;
	rz_list_foreach (threads, it, t) {
		RzDebugPid *newpid = RZ_NEW0(RzDebugPid);
		if (!newpid) {
			rz_list_free(threads);
			rz_list_free(ret);
			return NULL;
		}
		newpid->pid = t->uniqueid;
		newpid->status = t->status;
		newpid->runnable = t->runnable;
		rz_list_append(ret, newpid);
	}
	rz_list_free(threads);
	return ret;
}

static RzList *dmp_get_modules(DmpCtx *ctx) {
	if (ctx->type != DMP_DUMPTYPE_TRIAGE) {
		return winkd_list_modules(&ctx->windctx);
	}
	RzList *ret = rz_list_newf(winkd_windmodule_free);
	if (!ret) {
		return NULL;
	}
	struct rz_bin_dmp64_obj_t *obj = (struct rz_bin_dmp64_obj_t *)((RzBinFile *)ctx->bf)->o->bin_obj;
	RzListIter *it;
	dmp_driver_desc *driver;
	rz_list_foreach (obj->drivers, it, driver) {
		WindModule *mod = RZ_NEW0(WindModule);
		if (!mod) {
			rz_list_free(ret);
			return NULL;
		}
		mod->name = strdup(driver->file);
		mod->size = driver->size;
		mod->addr = driver->base;
		mod->timestamp = driver->timestamp;
		rz_list_append(ret, mod);
	}
	return ret;
}

static RzList *rz_debug_dmp_modules(RzDebug *dbg) {
	DmpCtx *ctx = dbg->plugin_data;
	RzList *ret = rz_list_newf((RzListFree)rz_debug_map_free);
	if (!ret) {
		return NULL;
	}
	RzList *modules = dmp_get_modules(ctx);
	RzListIter *it;
	WindModule *m;
	rz_list_foreach (modules, it, m) {
		RzDebugMap *mod = RZ_NEW0(RzDebugMap);
		if (!mod) {
			rz_list_free(modules);
			rz_list_free(ret);
			return NULL;
		}
		RZ_PTR_MOVE(mod->file, m->name);
		mod->name = strdup(rz_file_dos_basename(mod->file));
		mod->size = m->size;
		mod->addr = m->addr;
		mod->addr_end = m->addr + m->size;
		rz_list_append(ret, mod);
	}
	rz_list_free(modules);
	return ret;
}

static RzList *rz_debug_dmp_maps(RzDebug *dbg) {
	DmpCtx *ctx = dbg->plugin_data;
	RzList *maps = winkd_list_maps(&ctx->windctx);
	RzListIter *it;
	WindMap *m;
	RzList *ret = rz_list_newf((RzListFree)rz_debug_map_free);
	if (!ret) {
		rz_list_free(maps);
		return NULL;
	}
	rz_list_foreach (maps, it, m) {
		RzDebugMap *map = RZ_NEW0(RzDebugMap);
		if (!map) {
			rz_list_free(maps);
			rz_list_free(ret);
			return NULL;
		}
		if (m->file) {
			RZ_PTR_MOVE(map->file, m->file);
			map->name = strdup(rz_file_dos_basename(map->file));
		}
		map->size = m->end - m->start;
		map->addr = m->start;
		map->addr_end = m->end;
		map->perm = m->perm;
		rz_list_append(ret, map);
	}
	rz_list_free(maps);
	return ret;
}

static bool rz_debug_dmp_kill(RzDebug *dbg, int pid, int tid, int sig) {
	return true;
}

static int is_pc_inside_windmodule(const void *value, const void *list_data) {
	const ut64 pc = *(const ut64 *)value;
	const WindModule *module = list_data;
	return !(pc >= module->addr && pc < (module->addr + module->size));
}

typedef RzList *(*RzDebugFrameCallback)(RzDebug *dbg, ut64 at);

RzList *rz_debug_dmp_frames(RzDebug *dbg, ut64 at) {
	RzCore *core = dbg->corebind.core;
	DmpCtx *ctx = dbg->plugin_data;
	RzList *ret = NULL;
	if (!ctx->windctx.is_arm && ctx->windctx.is_64bit) {
		RzList *modules = NULL;
		struct context_type_amd64 context = { 0 };
		const char *server = dbg->corebind.cfgGet(dbg->corebind.core, "pdb.server");
		const char *symstore = dbg->corebind.cfgGet(dbg->corebind.core, "pdb.symstore");
		ut64 last_rsp = 0;
		while (!backtrace_windows_x64(dbg, &ret, &context)) {
			if (last_rsp == context.rsp) {
				break;
			}
			last_rsp = context.rsp;
			if (!modules) {
				modules = dmp_get_modules(ctx);
			}
			RzListIter *it = rz_list_find(modules, &context.rip, is_pc_inside_windmodule);
			if (!it) {
				break;
			}
			WindModule *module = rz_list_iter_get_data(it);
			char *exepath, *pdbpath;
			if (!winkd_download_module_and_pdb(module, server, symstore, &exepath, &pdbpath)) {
				break;
			}
			RzBinOptions opts = { 0 };
			opts.obj_opts.baseaddr = module->addr;
			RzBinFile *file = rz_bin_open(core->bin, exepath, &opts);
			if (!file) {
				free(exepath);
				free(pdbpath);
				break;
			}
			dbg->corebind.applyBinInfo(core, file, RZ_CORE_BIN_ACC_MAPS | RZ_CORE_BIN_ACC_SYMBOLS);
			dbg->corebind.cmdf(dbg->corebind.core, "idp %s", pdbpath);
			dbg->corebind.cmdf(dbg->corebind.core, "ompb %d", ((RzBinFile *)ctx->bf)->id);
			free(exepath);
			free(pdbpath);
		}
		rz_list_free(modules);
	} else {
		ret = backtrace_generic(dbg);
	}
	return ret;
}

RzDebugPlugin rz_debug_plugin_dmp = {
	.name = "dmp",
	.license = "LGPL3",
	.arch = "x86,arm",
	.bits = RZ_SYS_BITS_32 | RZ_SYS_BITS_64,
	.init = &rz_debug_dmp_init,
	.attach = &rz_debug_dmp_attach,
	.pids = &rz_debug_dmp_pids,
	.select = &rz_debug_dmp_select,
	.reg_read = &rz_debug_dmp_reg_read,
	.reg_profile = &rz_debug_dmp_reg_profile,
	.threads = &rz_debug_dmp_threads,
	.modules_get = &rz_debug_dmp_modules,
	.map_get = &rz_debug_dmp_maps,
	.kill = &rz_debug_dmp_kill,
	.frames = &rz_debug_dmp_frames,
};

#ifndef RZ_PLUGIN_INCORE
RZ_API RzLibStruct rizin_plugin = {
	.type = RZ_LIB_TYPE_DBG,
	.data = &rz_debug_plugin_dmp,
	.version = RZ_VERSION
};
#endif
