/* radare - LGPL - Copyright 2009-2024 - pancake */

#define R_LOG_ORIGIN "cfile"

#include <r_core.h>

#define UPDATE_TIME(a) (r->times->file_open_time = r_time_now_mono () - (a))

static int r_core_file_load_for_debug(RCore *r, ut64 loadaddr, const char *filenameuri);
static int r_core_file_load_for_io_plugin(RCore *r, ut64 baseaddr, ut64 loadaddr);

static bool close_but_cb(void *user, void *data, ut32 id) {
	RCore *core = (RCore *)user;
	RIODesc *desc = (RIODesc *)data;
	if (core && desc && core->io->desc) {
		if (desc->fd != core->io->desc->fd) {
			// TODO: use the API
			r_core_cmdf (core, "o-%d", desc->fd);
		}
	}
	return true;
}

// TODO: move to IO as a helper?
R_API bool r_core_file_close_all_but(RCore *core) {
	r_id_storage_foreach (&core->io->files, close_but_cb, core);
	return true;
}

static bool its_a_mips(RCore *core) {
	RArchConfig *cfg = core->rasm->config;
	return cfg && r_str_startswith (cfg->arch, "mips");
}

static void load_gp(RCore *core) {
	// R2R db/cmd/cmd_eval
	if (its_a_mips (core)) {
		ut64 e0 = r_num_math (core->num, "entry0");
		ut64 gp = r_num_math (core->num, "loc._gp");
		if ((!gp || gp == UT64_MAX) && (e0 && e0 != UT64_MAX)) {
			r_core_cmd0 (core, "aeim;s entry0;dr PC=entry0");
			r_config_set (core->config, "anal.roregs", "zero"); // gp is writable here
			r_core_cmd0 (core, "10aes");
			gp = r_reg_getv (core->anal->reg, "gp");
			r_core_cmd0 (core, "dr0;aeim");
			r_reg_setv (core->anal->reg, "gp", gp);
			r_config_set (core->config, "anal.roregs", "zero,gp");
		}
		R_LOG_DEBUG ("[mips] gp: 0x%08"PFMT64x, gp);
		r_config_set_i (core->config, "anal.gp", gp);
	}
}

R_API bool r_core_file_reopen(RCore *core, const char *args, int perm, int loadbin) {
	const bool isdebug = r_config_get_b (core->config, "cfg.debug");
	char *path;
	ut64 laddr = r_config_get_i (core->config, "bin.laddr");
	RIODesc *odesc = core->io ? core->io->desc : NULL;
	RBinFile *bf = odesc ? r_bin_file_find_by_fd (core->bin, odesc->fd) : NULL;
	char *ofilepath = NULL, *obinfilepath = (bf && bf->file)? strdup (bf->file): NULL;
	bool ret = false;
	ut64 origoff = core->addr;
	if (odesc) {
		if (odesc->referer) {
			ofilepath = odesc->referer;
		} else if (odesc->uri) {
			ofilepath = odesc->uri;
		}
	}

	ut64 new_baddr = UT64_MAX;
	if (args) {
		new_baddr = r_num_math (core->num, args);
		if (new_baddr && new_baddr != UT64_MAX) {
			r_config_set_i (core->config, "bin.baddr", new_baddr);
		} else {
			new_baddr = UT64_MAX;
		}
	}
	if (new_baddr == UT64_MAX) {
		if (r_config_get_b (core->config, "bin.aslr")) {
			new_baddr = ((ut64)r_num_rand (32)) << 24;
			r_config_set_i (core->config, "bin.baddr", new_baddr);
		} else {
			new_baddr = r_config_get_i (core->config, "bin.baddr");
		}
	}

	if (r_sandbox_enable (0)) {
		R_LOG_ERROR ("Cannot reopen in sandbox");
		free (obinfilepath);
		return false;
	}
	if (!odesc) {
		R_LOG_ERROR ("No file opened to reopen");
		free (ofilepath);
		free (obinfilepath);
		return false;
	}
	int newpid = odesc->fd;

	if (isdebug) {
		r_debug_kill (core->dbg, core->dbg->pid, core->dbg->tid, 9); // SIGKILL
		do {
			r_debug_continue (core->dbg);
		} while (!r_debug_is_dead (core->dbg));
		r_debug_detach (core->dbg, core->dbg->pid);
		perm = R_PERM_RWX;
	} else {
		if (!perm) {
			perm = R_PERM_R; // 4
		}
	}
	if (!ofilepath) {
		R_LOG_ERROR ("Unknown file path");
		free (obinfilepath);
		return false;
	}

	// HACK: move last mapped address to higher place
	// XXX - why does this hack work?
	// when the new memory maps are created.
	path = r_str_trim_dup (ofilepath);
	free (obinfilepath);
	obinfilepath = r_str_trim_dup (ofilepath);

	RIODesc *file = r_core_file_open (core, path, perm, laddr);
	if (isdebug) {
		int newtid = newpid;
		if (core->io->desc) {
			newpid = r_io_fd_get_pid (core->io, core->io->desc->fd);
#if __linux__
			core->dbg->main_pid = newpid;
			newtid = newpid;
#else
			newtid = r_io_fd_get_tid (core->io, core->io->desc->fd);
#endif
		}
		// Reset previous pid and tid
		core->dbg->pid = -1;
		core->dbg->tid = -1;
		core->dbg->recoil_mode = R_DBG_RECOIL_NONE;
		memset (&core->dbg->reason, 0, sizeof (core->dbg->reason));
		// Reopen and attach
		r_core_setup_debugger (core, "native", true);
		r_debug_select (core->dbg, newpid, newtid);
	}

	if (file) {
		bool had_rbin_info = false;

		if (odesc && bf) {
			if (r_bin_file_delete (core->bin, bf->id)) {
				had_rbin_info = true;
			}
		}
		r_io_fd_close (core->io, odesc->fd);
		R_LOG_INFO ("File %s reopened in %s mode", path, (perm & R_PERM_W)? "read-write": "read-only");
		if (loadbin && (loadbin == 2 || had_rbin_info)) {
			ut64 baddr;
			if (isdebug) {
				baddr = r_debug_get_baddr (core->dbg, path);
			} else if (new_baddr != UT64_MAX) {
				baddr = new_baddr;
			} else {
				if (r_config_get_b (core->config, "bin.aslr")) {
					baddr = r_num_rand (32) << 24;
					r_config_set_i (core->config, "bin.baddr", baddr);
				} else {
					baddr = r_config_get_i (core->config, "bin.baddr");
				}
			}
			ret = r_core_bin_load (core, obinfilepath, baddr);
			r_core_bin_update_arch_bits (core);
			if (!ret) {
				R_LOG_ERROR ("Failed to reload rbin for: %s", path);
			}
			origoff = r_num_math (core->num, "entry0");
		}

		if (core->bin->cur && core->io && r_io_desc_get (core->io, file->fd) && !loadbin) {
			//force here NULL because is causing uaf look this better in future XXX @alvarofe
			core->bin->cur = NULL;
		}
		// close old file
	} else if (odesc) {
		R_LOG_ERROR ("Cannot reopen file: %s with perms 0x%x, attempting to open read-only", path, perm);
		// lower it down back
		//ofile = r_core_file_open (core, path, R_PERM_R, addr);
	} else {
		R_LOG_ERROR ("Cannot reopen");
	}
	if (core->io->desc) {
		core->switch_file_view = 1;
		r_core_block_read (core);
	}
	r_core_seek (core, origoff, true);
	if (isdebug) {
		r_core_cmd0 (core, "arp>$_"); // Fixes a bug where registers are not synced wtf
		// r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, false); // does nothing
		r_core_cmd0 (core, ".dm*");
		r_core_cmd0 (core, ".dr*");
		r_core_cmd_call (core, "sr PC");
	} else {
		load_gp (core);
	}
	// update anal io bind
	r_io_bind (core->io, &(core->anal->iob));
	if (core->io->desc && core->io->desc->fd >= 0) {
		r_core_cmd0 (core, "o-!");
	}
	r_core_file_close_all_but (core);
	// This is done to ensure that the file is correctly
	// loaded into the view
	free (obinfilepath);
	//free (ofilepath);
	// causes double free . dont free file here // R_FREE (file);
	free (path);
	return ret;
}

R_API void r_core_sysenv_end(RCore *core, const char *cmd) {
	// TODO: remove tmpfilez
	if (strstr (cmd, "R2_BLOCK")) {
		// remove temporary BLOCK file
		char *f = r_sys_getenv ("R2_BLOCK");
		if (f) {
			r_file_rm (f);
			r_sys_setenv ("R2_BLOCK", NULL);
			free (f);
		}
	}
	r_sys_setenv ("R2_FILE", NULL);
	r_sys_setenv ("R2_BYTES", NULL);
	r_sys_setenv ("R2_OFFSET", NULL);
	r_sys_setenv ("R2_UTF8", NULL);

	// remove temporary R2_CONFIG file
	char *r2_config = r_sys_getenv ("R2_CONFIG");
	if (r2_config) {
		r_file_rm (r2_config);
		r_sys_setenv ("R2_CONFIG", NULL);
		free (r2_config);
	}
}

R_API char *r_core_sysenv_begin(RCore *core, const char *cmd) {
	char *f, *ret = cmd? strdup (cmd): NULL;
	RIODesc *desc = core->io->desc;
	if (cmd && strstr (cmd, "R2_BYTES")) {
		char *s = r_hex_bin2strdup (core->block, core->blocksize);
		r_sys_setenv ("R2_BYTES", s);
		free (s);
	}
	r_strf_buffer (64);
	r_sys_setenv ("RABIN2_PDBSERVER", r_config_get (core->config, "pdb.server"));
	char *r2file = r_core_cmd_str (core, "o.");
	r_str_trim (r2file);
	r_sys_setenv ("R2_FILE", r2file);
	free (r2file);
	if (desc && desc->name) {
		r_sys_setenv ("R2_SIZE", r_strf ("%"PFMT64d, r_io_desc_size (desc)));
		if (cmd && strstr (cmd, "R2_BLOCK")) {
			// replace BLOCK in RET string
			if ((f = r_file_temp ("r2block"))) {
				if (r_file_dump (f, core->block, core->blocksize, 0)) {
					r_sys_setenv ("R2_BLOCK", f);
				}
				free (f);
			}
		}
	}
	r_sys_setenv ("R2_COLOR", r_config_get (core->config, "scr.color"));

	r_sys_setenv ("R2_ADDR", r_strf ("%"PFMT64d, core->addr));
	r_sys_setenv ("R2_XADDR", r_strf ("0x%08"PFMT64x, core->addr));
#if 0
	r_sys_setenv ("R2_OFFSET", r_strf ("%"PFMT64d, core->addr));
	r_sys_setenv ("R2_XOFFSET", r_strf ("0x%08"PFMT64x, core->addr));
#endif
	r_sys_setenv ("R2_BADDR", r_strf ("0x%08"PFMT64x, r_config_get_i (core->config, "bin.baddr")));
	r_sys_setenv ("R2_ENDIAN", R_ARCH_CONFIG_IS_BIG_ENDIAN (core->rasm->config)? "big": "little");	// XXX
	r_sys_setenv ("R2_BSIZE", r_strf ("%d", core->blocksize));
#if 0
	// dump current config file so other r2 tools can use the same options
	char *config_sdb_path = NULL;
	int config_sdb_fd = r_file_mkstemp (NULL, &config_sdb_path);
	if (config_sdb_fd >= 0) {
		close (config_sdb_fd);
	}
	Sdb *config_sdb = sdb_new (NULL, config_sdb_path, 0);
	r_config_serialize (core->config, config_sdb);
	sdb_sync (config_sdb);
	sdb_free (config_sdb);
	r_sys_setenv ("R2_CONFIG", config_sdb_path);
#endif
	r_sys_setenv ("RABIN2_LANG", r_config_get (core->config, "bin.lang"));
	r_sys_setenv ("RABIN2_DEMANGLE", r_config_get (core->config, "bin.demangle"));
	r_sys_setenv ("R2_ARCH", r_config_get (core->config, "asm.arch"));
	r_sys_setenv ("R2_BITS", r_strf ("%"PFMT64u, r_config_get_i (core->config, "asm.bits")));
	char *s = sdb_itoas (r_config_get_i (core->config, "scr.color"), 10);
	r_sys_setenv ("R2_COLOR", s);
	free (s);
	r_sys_setenv ("R2_UTF8", r_config_get_b (core->config, "scr.utf8")? "1": "0");
	r_sys_setenv ("R2_IOVA", r_config_get_b (core->config, "io.va")? "1": "0");
#if 0
	free (config_sdb_path);
#endif
	return ret;
}

#if !__linux__ && !R2__WINDOWS__
static bool is_base_map(RDebugMap *map) {
	if ((map->perm & 1) == 1) { // "--x"
		// only used by FreeBSD (and PS5)
		return true;
	}
	if ((map->perm & 5) == 5) { // "r-x"
		return true;
	}
	return false;
}
static ut64 get_base_from_maps(RCore *core, const char *file) {
	RDebugMap *map;
	RListIter *iter;
	ut64 b = 0LL;

	r_debug_map_sync (core->dbg); // update process memory maps
	r_list_foreach (core->dbg->maps, iter, map) {
		if (is_base_map (map)) {
			// TODO: make this more flexible
			// XXX - why "copy/" here?
			if (map->name && strstr (map->name, "copy/")) {
				return map->addr;
			}
			if (file) {
				if (map->file && !strcmp (map->file, file)) {
					return map->addr;
				}
				if (map->name && !strcmp (map->name, file)) {
					return map->addr;
				}
			}
			// XXX - Commented out, as this could unexpected results
			//b = map->addr;
		}
	}
	// fallback resolution copied from cmd_debug.c:r_debug_get_baddr
	r_list_foreach (core->dbg->maps, iter, map) {
		if (is_base_map (map)) {
			return map->addr;
		}
	}

	return b;
}
#endif

#if __linux__ || __APPLE__
static bool setbpint(RCore *r, const char *mode, const char *sym) {
	RBreakpointItem *bp;
	RFlagItem *fi = r_flag_get (r->flags, sym);
	if (!fi) {
		return false;
	}
	bp = r_bp_add_sw (r->dbg->bp, fi->addr, 1, R_BP_PROT_EXEC);
	if (bp) {
		bp->internal = true;
#if __linux__
		bp->data = r_str_newf ("?e %s: %s", mode, sym);
#else
		bp->data = r_str_newf ("?e %s: %s;ps@rdi", mode, sym);
#endif
		return true;
	}
	R_LOG_ERROR ("Cannot set breakpoint at %s", sym);
	return false;
}
#endif

// XXX - need to handle index selection during debugging
static int r_core_file_load_for_debug(RCore *r, ut64 baseaddr, const char * R_NULLABLE filenameuri) {
	RIODesc *desc = r->io->desc;
	RBinFile *bf = NULL;
	RBinPlugin *plugin;
	int xtr_idx = 0; // if 0, load all if xtr is used

	// TODO : Honor file.path eval var too?
	if (filenameuri) {
		if (r_str_startswith (filenameuri, "dbg://")) {
			filenameuri += 6;
		} else if (r_str_startswith (filenameuri, "sysgdb://")) {
			filenameuri += 9;
		}
	}
	if (!desc) {
		return false;
	}
	int fd = desc->fd;
	r_debug_select (r->dbg, r_io_fd_get_pid (r->io, fd),
			r_io_fd_get_tid (r->io, fd));
#if !__linux__
#if !R2__WINDOWS__
	baseaddr = get_base_from_maps (r, filenameuri);
#endif
	if (baseaddr != UT64_MAX) {
		r_config_set_i (r->config, "bin.baddr", baseaddr);
	}
#endif
	RBinFileOptions opt;
	r_bin_file_options_init (&opt, fd, baseaddr, UT64_MAX, false);
	opt.xtr_idx = xtr_idx;
	if (!r_bin_open (r->bin, filenameuri, &opt)) {
		R_LOG_ERROR ("bin.open failed %s", filenameuri);
		if (r_config_get_b (r->config, "bin.str.raw")) {
			r_bin_file_options_init (&opt, fd, baseaddr, UT64_MAX, true);
			opt.xtr_idx = xtr_idx;
			if (!r_bin_open (r->bin, filenameuri, &opt)) {
				return false;
			}
		}
	}

	if (*r_config_get (r->config, "dbg.libs")) {
		r_core_cmd0 (r, ".dmm*");
#if __linux__
		setbpint (r, "dbg.libs", "sym.imp.dlopen");
		setbpint (r, "dbg.libs", "sym.imp.dlmopen");
		setbpint (r, "dbg.unlibs", "sym.imp.dlclose");
#elif __APPLE__
		setbpint (r, "dbg.libs", "sym._dlopen");
		setbpint (r, "dbg.libs", "sym._dlclose");
#endif
	}
	bf = r_bin_cur (r->bin);
	r_core_bin_set_env (r, bf);
	plugin = r_bin_file_cur_plugin (bf);
	const char *plugin_name = plugin? plugin->meta.name: "";
	if (!strcmp (plugin_name, "any")) {
		// set use of raw strings
		// r_config_set_i (r->config, "io.va", false);
		// r_config_set_b (r->config, "bin.str.raw", true);
		// get bin.str.min
		r->bin->options.minstrlen = r_config_get_i (r->config, "bin.str.min");
		r->bin->options.maxstrbuf = r_config_get_i (r->config, "bin.str.maxbuf");
	} else if (bf) {
		RBinObject *obj = r_bin_cur_object (r->bin);
		RBinInfo *info = obj? obj->info: NULL;
		if (plugin && info) {
			r_core_bin_set_arch_bits (r, bf->file, info->arch, info->bits);
		}
	}
	if (!strcmp (plugin_name, "dex")) {
		r_core_cmd0 (r, "'(fix-dex;wx `ph sha1 $s-32 @32` @12 ; wx `ph adler32 $s-12 @12` @8)");
	}

	return true;
}

static int r_core_file_load_for_io_plugin(RCore *r, ut64 baseaddr, ut64 loadaddr) {
	RIODesc *cd = r->io->desc;
	int fd = cd ? cd->fd : -1;
	int xtr_idx = 0; // if 0, load all if xtr is used
	RBinPlugin *plugin;

	if (fd < 0) {
		return false;
	}
	R_CRITICAL_ENTER (r);
	r_io_use_fd (r->io, fd);
	if (baseaddr == UT64_MAX) {
		// ASLR - this is probably the only place where bin.aslr is used. maybe move into rbin.options before R2_600
		if (r_config_get_b (r->config, "bin.aslr")) {
			baseaddr = ((ut64)r_num_rand (32)) << 24;
			r_config_set_i (r->config, "bin.baddr", baseaddr);
		}
	}
	RBinFileOptions opt;
	r_bin_file_options_init (&opt, fd, baseaddr, loadaddr, r->bin->options.rawstr);
	// opt.fd = fd;
	opt.xtr_idx = xtr_idx;
	if (!r_bin_open_io (r->bin, &opt)) {
		R_CRITICAL_LEAVE (r);
		return false;
	}
	RBinFile *bf = r_bin_cur (r->bin);
	if (r_core_bin_set_env (r, bf)) {
		if (r->anal->verbose && !sdb_const_get (r->anal->sdb_cc, "default.cc", 0)) {
			R_LOG_WARN ("No calling convention defined for this file, analysis may be inaccurate");
		}
	}
	if (bf) {
		const char *bclass = R_UNWRAP4 (bf, bo, info, bclass);
		if (bclass && strstr (bclass, "://")) {
			// perform a redirection!
			char *uri = r_str_newf ("%s%s\n", bclass, bf->file);
			r_core_cmdf (r, "ob-*");
			r_core_cmdf (r, "o %s", uri);
			// r_core_cmdf (r, "o-%d", fd); // XXX segfault
			free (uri);
			// r_io_close_fd (r->io, fd);
			// r_bin_file_close (r->bin, bf);
			return false;
		}
	}
	plugin = r_bin_file_cur_plugin (bf);
	if (plugin && !strcmp (plugin->meta.name, "any")) {
		RBinObject *obj = r_bin_cur_object (r->bin);
		RBinInfo *info = obj? obj->info: NULL;
		if (!info) {
			R_CRITICAL_LEAVE (r);
			return false;
		}
		info->bits = r->rasm->config->bits;
		// set use of raw strings
		r_core_bin_set_arch_bits (r, bf->file, info->arch, info->bits);
		// r_config_set_i (r->config, "io.va", false);
		// r_config_set_b (r->config, "bin.str.raw", true);
		// get bin.str.min
		r->bin->options.minstrlen = r_config_get_i (r->config, "bin.str.min");
		r->bin->options.maxstrbuf = r_config_get_i (r->config, "bin.str.maxbuf");
	} else if (bf) {
		RBinObject *obj = r_bin_cur_object (r->bin);
		RBinInfo *info = obj? obj->info: NULL;
		if (!info) {
			R_CRITICAL_LEAVE (r);
			return false;
		}
		if (plugin) {
			r_core_bin_set_arch_bits (r, bf->file, info->arch, info->bits);
		}
	}

	if (plugin && !strcmp (plugin->meta.name, "dex")) {
		r_core_cmd0 (r, "'(fix-dex;wx `ph sha1 $s-32 @32` @12 ; wx `ph adler32 $s-12 @12` @8)");
	}
	R_CRITICAL_LEAVE (r);
	return true;
}

static bool try_loadlib(RCore *core, const char *lib, ut64 addr) {
	void *p = r_core_file_open (core, lib, 0, addr);
	if (p) {
		r_core_bin_load (core, lib, addr);
		return true;
	}
	return false;
}

R_API bool r_core_file_loadlib(RCore *core, const char *lib, ut64 libaddr) {
	const char *dirlibs = r_config_get (core->config, "dir.libs");
#ifdef R2__WINDOWS__
	char *libdir = r_str_r2_prefix (R2_LIBDIR);
	if (!libdir) {
		libdir = strdup (R2_LIBDIR);
	}
#else
	char *libdir = strdup (R2_LIBDIR);
#endif
	if (!dirlibs || !*dirlibs) {
		dirlibs = "." R_SYS_DIR;
	}
	const char *ldlibrarypath[] = {
		dirlibs,
		libdir,
#ifndef R2__WINDOWS__
		"/usr/local/lib",
		"/usr/lib",
		"/lib",
#endif
		"." R_SYS_DIR,
		NULL
	};
	const char * *libpath = (const char * *) &ldlibrarypath;

	bool ret = false;
#ifdef R2__WINDOWS__
	if (strlen (lib) >= 3 && lib[1] == ':' && lib[2] == '\\') {
#else
	if (*lib == '/') {
#endif
		if (try_loadlib (core, lib, libaddr)) {
			ret = true;
		}
	} else {
		while (*libpath) {
			char *s = r_str_newf ("%s" R_SYS_DIR "%s", *libpath, lib);
			if (try_loadlib (core, s, libaddr)) {
				ret = true;
			}
			free (s);
			if (ret) {
				break;
			}
			libpath++;
		}
	}
	free (libdir);
	return ret;
}

#if 0
static void load_scripts_for(RCore *core, const char *name) {
	// imho nobody uses this: run scripts depending on a specific filetype
	char *file;
	RListIter *iter;
	char *hdir = r_str_newf ("%s%s%s%sbin-%s", R2_HOME_BINRC, R_SYS_DIR, "bin-%s", R_SYS_DIR, name);
	char *path = r_file_home (hdir);
	RList *files = r_sys_dir (path);
	if (!r_list_empty (files)) {
		R_LOG_INFO ("[binrc] path: %s", path);
	}
	r_list_foreach (files, iter, file) {
		if (*file && *file != '.') {
			R_LOG_INFO ("[binrc] loading %s", file);
			r_core_cmdf (core, ". %s/%s", path, file);
		}
	}
	r_list_free (files);
	free (path);
	free (hdir);
}
#endif

typedef struct {
	const char *name;
	bool found;
} MyFileData;

static bool filecb(void *user, void *data, ut32 id) {
	MyFileData *filedata = user;
	RIODesc *desc = (RIODesc *)data;
	if (!strcmp (desc->name, filedata->name)) {
		filedata->found = true;
	}
	return true;
}

static bool file_is_loaded(RCore *core, const char *lib) {
	MyFileData filedata = {lib, false};
	r_id_storage_foreach (&core->io->files, filecb, &filedata);
	return filedata.found;
}

typedef struct {
	const char *name;
	ut64 addr;
	RBin *bin;
} RCoreLinkData;

static bool linkcb(void *user, void *data, ut32 id) {
	RCoreLinkData *ld = user;
	RIODesc *desc = (RIODesc *)data;

	RBinFile *bf = r_bin_file_find_by_fd (ld->bin, desc->fd);
	if (bf) {
		RBinSymbol *sym;
		RVecRBinSymbol *symbols = r_bin_file_get_symbols_vec (bf);
		R_VEC_FOREACH (symbols, sym) {
			const char *sname = r_bin_name_tostring (sym->name);
			if (!strcmp (sname, ld->name)) {
				ld->addr = sym->vaddr;
				return false;
			}
		}
	}
	return true;
}

static bool mustreopen(RCore *core, RIODesc *desc, const char *fn) {
	if (r_str_startswith (fn, "slurp://")) {
		return false;
	}
	if (r_str_startswith (fn, "stdio://")) {
		return false;
	}
	if (r_str_startswith (fn, "file://")) {
		return false;
	}
	if (r_str_startswith (fn, "nocache://")) {
		return false;
	}
	if (r_str_startswith (fn, "mmap://")) {
		return false;
	}
	if (fn && fn[0] != '-' && strcmp (fn, desc->uri)) {
		return true;
	}
	return false;
}

R_API bool r_core_bin_load(RCore *r, const char *filenameuri, ut64 baddr) {
	R_RETURN_VAL_IF_FAIL (r && r->io, false);
	R_CRITICAL_ENTER (r);
	ut64 laddr = r_config_get_i (r->config, "bin.laddr");
	RIODesc *desc = r->io->desc;
	if (!desc && filenameuri) {
		desc = r_io_desc_get_byuri (r->io, filenameuri);
	}
	if (!desc) {
		// hack for openmany handlers
		if (!filenameuri || *filenameuri == '-') {
			// filenameuri = "malloc://512";
			R_CRITICAL_LEAVE (r);
			return false;
		}
		r_core_file_open (r, filenameuri, 0, baddr);
		desc = r->io->desc;
	}
	bool is_io_load = false;
	if (desc && desc->plugin) {
		is_io_load = true;
		if (r_str_startswith (filenameuri, "frida://")) {
			// dont try to guess the bin file in the address 0 if we are using r2frida
			is_io_load = false;
		}
	}
	r->bin->options.minstrlen = r_config_get_i (r->config, "bin.str.min");
	r->bin->options.maxstrbuf = r_config_get_i (r->config, "bin.str.maxbuf");
	R_CRITICAL_LEAVE (r);
	RIODesc *odesc = NULL;
	RIODesc *mustclose = NULL;
	odesc = r->io->desc;
	if (desc && is_io_load) {
		int desc_fd = desc->fd;
		// TODO? necessary to restore the desc back?
		// Fix to select pid before trying to load the binary
		if ((desc->plugin && desc->plugin->isdbg) || r_config_get_b (r->config, "cfg.debug")) {
			r_core_file_load_for_debug (r, baddr, filenameuri);
		} else {
			if (mustreopen (r, desc, filenameuri)) {
				r_core_file_open (r, filenameuri, 0, baddr);
				if (odesc != r->io->desc) {
					mustclose = r->io->desc;
				}
			}
			r_core_file_load_for_io_plugin (r, baddr, 0LL);
			desc = r->io->desc;
		}
		r_io_use_fd (r->io, desc_fd);
	} else if (desc != NULL) {
		r_io_use_fd (r->io, desc->fd);
		r_io_map_add (r->io, desc->fd, R_PERM_RWX, 0LL, 0, r_io_desc_size (desc));
	}
	desc = r->io->desc;
	RBinFile *binfile = r_bin_cur (r->bin);
	RBinPlugin *bp = R_UNWRAP5 (r, bin, cur, bo, plugin);
	if (bp) {
		char msg[2];
		msg[0] = bp->strfilter;
		msg[1] = 0;
		r_config_set (r->config, "bin.str.filter", msg);
	}
	RBinPlugin *plugin = r_bin_file_cur_plugin (binfile);
#if 0
	//r_core_bin_set_env (r, binfile);
	if (plugin && plugin->name) {
		load_scripts_for (r, plugin->name);
	}
#endif
	r_core_bin_export_info (r, R_MODE_SET);
	const char *cmd_load = r_config_get (r->config, "cmd.load");
	if (R_STR_ISNOTEMPTY (cmd_load)) {
		r_core_cmd (r, cmd_load, 0);
	}

	if (desc && plugin && plugin->meta.name) {
		if (!strcmp (plugin->meta.name, "any")) {
			ut64 size = (desc->name && (r_str_startswith (desc->name, "rap") && strstr (desc->name, "://")))
				? UT64_MAX : r_io_desc_size (desc);
			r_io_map_add (r->io, desc->fd, desc->perm, 0, laddr, size);
			// set use of raw strings
			// r_config_set_b (r->config, "bin.str.raw", true);
			// r_config_set_b (r->config, "io.va", false);
			// get bin.str.min
			r->bin->options.minstrlen = r_config_get_i (r->config, "bin.str.min");
			r->bin->options.maxstrbuf = r_config_get_i (r->config, "bin.str.maxbuf");
		} else if (binfile) {
			RBinObject *obj = r_bin_cur_object (r->bin);
			if (obj) {
				bool va = obj->info ? obj->info->has_va : 0;
				if (!va) {
					r_config_set_i (r->config, "io.va", 0);
				}
				// workaround to map correctly malloc:// and raw binaries
				if (r_io_desc_is_dbg (desc) || (!obj->sections || !va)) {
					r_io_map_add (r->io, desc->fd, desc->perm, 0, laddr, r_io_desc_size (desc));
				}
				RBinInfo *info = obj->info;
				if (info) {
					r_core_bin_set_arch_bits (r, binfile->file, info->arch, info->bits);
				} else {
					r_core_bin_set_arch_bits (r, binfile->file,
						r_config_get (r->config, "asm.arch"),
						r_config_get_i (r->config, "asm.bits"));
				}
			}
		}
	} else {
		bool basemap = r_config_get_b (r->config, "io.basemap");
		if (desc && basemap) {
			r_io_map_add (r->io, desc->fd, desc->perm, 0, laddr, r_io_desc_size (desc));
		}
		if (binfile) {
			r_core_bin_set_arch_bits (r, binfile->file,
					r_config_get (r->config, "asm.arch"),
					r_config_get_i (r->config, "asm.bits"));
		}
	}
	if (desc && r_config_get_i (r->config, "io.exec")) {
		desc->perm |= R_PERM_X;
	}
	if (plugin && plugin->meta.name && !strcmp (plugin->meta.name, "dex")) {
		r_core_cmd0 (r, "'(fix-dex;wx `ph sha1 $s-32 @32` @12;wx `ph adler32 $s-12 @12` @8)");
	}
	if (!r_config_get_b (r->config, "cfg.debug")) {
		load_gp (r);
	}
	if (r_config_get_b (r->config, "bin.libs")) {
		const char *lib;
		RListIter *iter;
		RList *libs = r_bin_get_libs (r->bin);
		r_list_foreach (libs, iter, lib) {
			if (file_is_loaded (r, lib)) {
				continue;
			}
			R_LOG_INFO ("[bin.libs] Opening %s", lib);
			ut64 baddr = (r->io->bits == 64)? 0x60000000000LL: 0x60000000;	// do we really need io->bits?
			if (r_io_map_locate (r->io, &baddr, 0x200000, 1)) {
				r_core_file_loadlib (r, lib, baddr);
			}
		}
		r_core_cmd0 (r, "obb 0;s entry0");
		r_config_set_i (r->config, "bin.at", true);
		R_LOG_INFO ("[bin.libs] Linking imports");
		RBinImport *imp;
		const RList *imports = r_bin_get_imports (r->bin);
		r_list_foreach (imports, iter, imp) {
			// PLT finding
			char *flagname = r_str_newf ("sym.imp.%s", r_bin_name_tostring2 (imp->name, 'f'));
			RFlagItem *impsym = r_flag_get (r->flags, flagname);
			free (flagname);
			if (!impsym) {
				// R_LOG_ERROR ("Cannot find '%s' import in the PLT", imp->name);
				continue;
			}
			ut64 imp_addr = impsym->addr;
			const char *imp_name = r_bin_name_tostring2 (imp->name, 'o');
			eprintf ("Resolving %s... ", imp_name);
			RCoreLinkData linkdata = {imp_name, UT64_MAX, r->bin};
			r_id_storage_foreach (&r->io->files, linkcb, &linkdata);
			if (linkdata.addr != UT64_MAX) {
				eprintf ("0x%08"PFMT64x, linkdata.addr);
				const ut64 a = linkdata.addr;
				const ut64 b = imp_addr;
				r_core_cmdf (r, "ax 0x%08"PFMT64x" 0x%08"PFMT64x, a, b);
			}
		}
	}
	bool is_core = false;
	if (plugin && plugin->info) {
		RBinInfo *info = plugin->info (binfile);
		if (info && info->type) {
			is_core = strstr (info->type, "CORE");
		}
		r_bin_info_free (info);
	}
	// the bintypecore is a wrong callback that is used only by ELF and must be deprecated. info should be in RBinInfo instead
	// if type == R_BIN_TYPE_CORE, we need to create all the maps
	if (plugin && binfile && is_core) {
		ut64 sp_addr = UT64_MAX;
		RIOMap *stack_map = NULL;
		// Setting the right arch and bits, so regstate will be shown correctly
		if (plugin->info) {
			RBinInfo *inf = plugin->info (binfile);
			R_LOG_INFO ("Setting up coredump arch-bits to: %s-%d", inf->arch, inf->bits);
			r_config_set (r->config, "asm.arch", inf->arch);
			r_config_set_i (r->config, "asm.bits", inf->bits);
			r_bin_info_free (inf);
		}
		if (binfile->bo->regstate) {
			if (r_reg_arena_set_bytes (r->anal->reg, binfile->bo->regstate)) {
				R_LOG_INFO ("Setting up coredump: Problem while setting the registers");
			} else {
				R_LOG_INFO ("Setting up coredump: Registers have been set");
				const char *regname = r_reg_alias_getname (r->anal->reg, R_REG_ALIAS_SP);
				if (regname) {
					RRegItem *reg = r_reg_get (r->anal->reg, regname, -1);
					if (reg) {
						sp_addr = r_reg_get_value (r->anal->reg, reg);
						stack_map = r_io_map_get_at (r->io, sp_addr);
					}
				}
				regname = r_reg_alias_getname (r->anal->reg, R_REG_ALIAS_PC);
				if (regname) {
					RRegItem *reg = r_reg_get (r->anal->reg, regname, -1);
					if (reg) {
						ut64 seek = r_reg_get_value (r->anal->reg, reg);
						r_core_seek (r, seek, true);
					}
				}
			}
		}

		RBinObject *o = binfile->bo;
		int map = 0;
		if (o && o->maps) {
			RList *maps = o->maps;
			RListIter *iter;
			RBinMap *mapcore;

			r_list_foreach (maps, iter, mapcore) {
				RIOMap *iomap = r_io_map_get_at (r->io, mapcore->addr);
				if (iomap && (mapcore->file || stack_map == iomap)) {
					r_io_map_set_name (iomap, r_str_get_fail (mapcore->file, "[stack]"));
				}
				map++;
			}
			r_list_free (maps);
			o->maps = NULL;
		}
		R_LOG_INFO ("Setting up coredump: %d maps have been found and created", map);
		goto beach;
	}
beach:
	if (r_config_get_b (r->config, "bin.dbginfo") && R_STR_ISNOTEMPTY (filenameuri)) {
		// TODO only for macho
		// load companion dwarf files
		const char *basename = r_file_basename (filenameuri);
		char *macdwarf = r_str_newf ("%s.dSYM/Contents/Resources/DWARF/%s", filenameuri, basename);
		if (r_file_exists (macdwarf)) {
			// RBinObject *obj = r_bin_cur_object (r->bin);
			// ut64 nbaddr = obj? obj->baddr: baddr;
			r_core_cmd_callf (r, "o %s", macdwarf);
			r_core_cmd_call (r, "obm-");
			// r_core_cmd_callf (r, "o-."); // causes uaf
		}
		free (macdwarf);
	}
	if (mustclose) {
	//	r_io_desc_close (mustclose);
		if (odesc) {
			r_io_use_fd (r->io, odesc->fd);
		}
	}
	r_flag_space_set (r->flags, "*");
	return true;
}

R_API RIODesc *r_core_file_open_many(RCore *r, const char *file, int perm, ut64 loadaddr) {
	RIODesc *fd;

	RList *list_fds = r_io_open_many (r->io, file, perm, 0644);

	if (!list_fds || r_list_length (list_fds) == 0) {
		r_list_free (list_fds);
		return NULL;
	}
	RListIter *iter;
	RIODesc *first = NULL;
	bool incloadaddr = loadaddr == 0;
	// r_config_set_b (r->config, "io.va", false);
	r_list_foreach (list_fds, iter, fd) {
		if (fd->uri) {
			if (!first) {
				first = fd;
			}
			r_io_use_fd (r->io, fd->fd);
			// r_core_file_open (r, fd->uri, perm, loadaddr);
			ut64 sz = r_io_fd_size (r->io, fd->fd);
			r_core_bin_load (r, fd->uri, loadaddr);
			if (incloadaddr && sz != UT64_MAX) {
				const int rest = (sz % 4096);
				const int pillow = 0x4000;
				loadaddr += sz + rest + pillow;
			}
			r_esil_setup (r->anal->esil, r->anal, 0, 0, false);
		}
	}
	return first;
}

/* loadaddr is r2 -m (mapaddr) */
R_API RIODesc *r_core_file_open(RCore *r, const char *file, int flags, ut64 loadaddr) {
	R_RETURN_VAL_IF_FAIL (r && file, NULL);
	ut64 prev = r_time_now_mono ();

	if (!strcmp (file, "-")) {
		file = "malloc://512";
	}
	//if not flags was passed open it with -r--
	if (!flags) {
		flags = R_PERM_R;
	}
	r->io->bits = r->rasm->config->bits; // TODO: we need an api for this
	RIODesc *fd = r_io_open_nomap (r->io, file, flags, 0644);
	if (r_cons_is_breaked (r->cons)) {
		goto beach;
	}
	if (!fd) {
		// XXX - make this an actual option somewhere?
		fd = r_core_file_open_many (r, file, flags, loadaddr);
		if (fd) {
			goto beach;
		}
	}
	if (!fd) {
		if (flags & R_PERM_W) {
		//	flags |= R_IO_CREAT;
			if (!(fd = r_io_open_nomap (r->io, file, flags, 0644))) {
				goto beach;
			}
		} else {
			goto beach;
		}
	}
	if (r_io_is_listener (r->io)) {
		r_core_serve (r, fd);
		r_io_desc_free (fd);
		fd = NULL;
		goto beach;
	}
	{
		const char *cp = r_config_get (r->config, "cmd.open");
		if (cp && *cp) {
			r_core_cmd (r, cp, 0);
		}
	}

	r_io_use_fd (r->io, fd->fd);

	r_esil_setup (r->anal->esil, r->anal, 0, 0, false);
	if (r_config_get_b (r->config, "cfg.debug")) {
		RDebugPlugin *plugin = R_UNWRAP3 (r->dbg, current, plugin);
		const bool swstep = (plugin && plugin->canstep)? false: true;
		r_config_set_b (r->config, "dbg.swstep", swstep);
		// Set the correct debug handle
		if (fd->plugin && fd->plugin->isdbg) {
			char *dh = r_str_ndup (file, (strstr (file, "://") - file));
			if (dh) {
				r_debug_use (r->dbg, dh);
				free (dh);
			}
		}
	}
	// used by r_core_bin_load otherwise won't load correctly
	// this should be argument of r_core_bin_load <shrug>
	if (loadaddr != UT64_MAX) {
		r_config_set_i (r->config, "bin.laddr", loadaddr);
	}
	r_core_cmd0 (r, "=!");
beach:
	r->times->file_open_time = r_time_now_mono () - prev;
	return fd;
}
