793 lines
21 KiB
C
793 lines
21 KiB
C
// SPDX-FileCopyrightText: 2008-2019 condret <condr3t@protonmail.com>
|
|
// SPDX-FileCopyrightText: 2008-2019 pancake <pancake@nopcode.org>
|
|
// SPDX-FileCopyrightText: 2008-2019 alvaro_fe <alvaro.felipe91@gmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_io.h>
|
|
#include <sdb.h>
|
|
#include "io_private.h"
|
|
#include <rz_util/rz_assert.h>
|
|
#include <rz_util/rz_buf.h>
|
|
#include <rz_util/rz_log.h>
|
|
|
|
#if __WINDOWS__
|
|
#include <rz_windows.h>
|
|
#include <w32dbg_wrap.h>
|
|
#endif
|
|
|
|
RZ_LIB_VERSION(rz_io);
|
|
|
|
static int fd_read_at_wrap(RzIO *io, int fd, ut64 addr, ut8 *buf, size_t len, RzIOMap *map, void *user) {
|
|
return rz_io_fd_read_at(io, fd, addr, buf, len);
|
|
}
|
|
|
|
static int fd_write_at_wrap(RzIO *io, int fd, ut64 addr, ut8 *buf, size_t len, RzIOMap *map, void *user) {
|
|
return rz_io_fd_write_at(io, fd, addr, buf, len);
|
|
}
|
|
|
|
typedef int (*cbOnIterMap)(RzIO *io, int fd, ut64 addr, ut8 *buf, size_t len, RzIOMap *map, void *user);
|
|
|
|
// If prefix_mode is true, returns the number of bytes of operated prefix; returns < 0 on error.
|
|
// If prefix_mode is false, operates in non-stop mode and returns true iff all IO operations on overlapped maps are complete.
|
|
static st64 on_map_skyline(RzIO *io, ut64 vaddr, ut8 *buf, size_t len, int match_flg, cbOnIterMap op, bool prefix_mode) {
|
|
RzVector *skyline = &io->map_skyline.v;
|
|
ut64 addr = vaddr;
|
|
size_t i;
|
|
bool ret = false, wrap = !prefix_mode && vaddr + len < vaddr;
|
|
#define CMP(addr, part) ((addr) < rz_itv_end(((RzSkylineItem *)(part))->itv) - 1 ? -1 : (addr) > rz_itv_end(((RzSkylineItem *)(part))->itv) - 1 ? 1 \
|
|
: 0)
|
|
// Let i be the first skyline part whose right endpoint > addr
|
|
if (!len) {
|
|
i = rz_vector_len(skyline);
|
|
} else {
|
|
rz_vector_lower_bound(skyline, addr, i, CMP);
|
|
if (i == rz_vector_len(skyline) && wrap) {
|
|
wrap = false;
|
|
i = 0;
|
|
addr = 0;
|
|
}
|
|
}
|
|
#undef CMP
|
|
while (i < rz_vector_len(skyline)) {
|
|
const RzSkylineItem *part = rz_vector_index_ptr(skyline, i);
|
|
// Right endpoint <= addr
|
|
if (rz_itv_end(part->itv) == 0 || rz_itv_end(part->itv) - 1 < addr) {
|
|
i++;
|
|
if (wrap && i == rz_vector_len(skyline)) {
|
|
wrap = false;
|
|
i = 0;
|
|
addr = 0;
|
|
}
|
|
continue;
|
|
}
|
|
if (addr < part->itv.addr) {
|
|
// [addr, part->itv.addr) is a gap
|
|
if (prefix_mode || len <= part->itv.addr - vaddr) {
|
|
break;
|
|
}
|
|
addr = part->itv.addr;
|
|
}
|
|
// Now left endpoint <= addr < right endpoint
|
|
ut64 len1 = RZ_MIN(vaddr + len - addr, rz_itv_end(part->itv) - addr);
|
|
if (len1 < 1) {
|
|
break;
|
|
}
|
|
RzIOMap *map = part->user;
|
|
// The map satisfies the permission requirement or p_cache is enabled
|
|
if (((map->perm & match_flg) == match_flg || io->p_cache)) {
|
|
st64 result = op(io, map->fd, map->delta + addr - map->itv.addr,
|
|
buf + (addr - vaddr), len1, map, NULL);
|
|
if (prefix_mode) {
|
|
if (result < 0) {
|
|
return result;
|
|
}
|
|
addr += result;
|
|
if (result != len1) {
|
|
break;
|
|
}
|
|
} else {
|
|
ret = result == len1;
|
|
addr += len1;
|
|
}
|
|
} else if (prefix_mode) {
|
|
break;
|
|
} else {
|
|
addr += len1;
|
|
ret = false;
|
|
}
|
|
// Reaches the end
|
|
if (addr == vaddr + len) {
|
|
break;
|
|
}
|
|
// Wrap to the beginning of skyline if address wraps
|
|
if (!addr) {
|
|
i = 0;
|
|
}
|
|
}
|
|
return prefix_mode ? addr - vaddr : ret;
|
|
}
|
|
|
|
RZ_API RzIO *rz_io_new(void) {
|
|
return rz_io_init(RZ_NEW0(RzIO));
|
|
}
|
|
|
|
RZ_API RzIO *rz_io_init(RzIO *io) {
|
|
rz_return_val_if_fail(io, NULL);
|
|
rz_io_desc_init(io);
|
|
rz_skyline_init(&io->map_skyline);
|
|
rz_io_map_init(io);
|
|
rz_io_cache_init(io);
|
|
rz_io_plugin_init(io);
|
|
io->event = rz_event_new(io);
|
|
return io;
|
|
}
|
|
|
|
RZ_API void rz_io_free(RzIO *io) {
|
|
if (io) {
|
|
rz_io_fini(io);
|
|
free(io);
|
|
}
|
|
}
|
|
|
|
RZ_API RzIODesc *rz_io_open_buffer(RzIO *io, RzBuffer *b, int perm, int mode) {
|
|
ut64 bufSize = rz_buf_size(b);
|
|
char *uri = rz_str_newf("malloc://%" PFMT64d, bufSize);
|
|
RzIODesc *desc = rz_io_open_nomap(io, uri, perm, mode);
|
|
if (desc) {
|
|
const ut8 *tmp = rz_buf_data(b, &bufSize);
|
|
rz_io_desc_write(desc, tmp, bufSize);
|
|
}
|
|
free(uri);
|
|
return desc;
|
|
}
|
|
|
|
RZ_API RzIODesc *rz_io_open_nomap(RzIO *io, const char *uri, int perm, int mode) {
|
|
rz_return_val_if_fail(io && uri, NULL);
|
|
RzIODesc *desc = rz_io_desc_open(io, uri, perm, mode);
|
|
if ((io->autofd || !io->desc) && desc) {
|
|
io->desc = desc;
|
|
}
|
|
// set desc as current if autofd or io->desc==NULL
|
|
return desc;
|
|
}
|
|
|
|
/* opens a file and maps it to 0x0 */
|
|
RZ_API RzIODesc *rz_io_open(RzIO *io, const char *uri, int perm, int mode) {
|
|
rz_return_val_if_fail(io, NULL);
|
|
RzIODesc *desc = rz_io_open_nomap(io, uri, perm, mode);
|
|
if (!desc) {
|
|
return NULL;
|
|
}
|
|
rz_io_map_new(io, desc->fd, desc->perm, 0LL, 0LL, rz_io_desc_size(desc));
|
|
return desc;
|
|
}
|
|
|
|
/**
|
|
* \brief Open a file and directly map it at the given offset
|
|
*
|
|
* This executes both rz_io_open_nomap() and rz_io_map_new() and returns their
|
|
* results without updating the skyline.
|
|
*
|
|
* \param uri file uri to open
|
|
* \param at where to map the file
|
|
* \param map optionally returns the created RzIOMap
|
|
* \return the opened RzIODesc of the file
|
|
* */
|
|
RZ_API RzIODesc *rz_io_open_at(RzIO *io, const char *uri, int perm, int mode, ut64 at, RZ_NULLABLE RZ_OUT RzIOMap **map) {
|
|
rz_return_val_if_fail(io && uri, NULL);
|
|
|
|
RzIODesc *desc = rz_io_open_nomap(io, uri, perm, mode);
|
|
if (!desc) {
|
|
return NULL;
|
|
}
|
|
ut64 size = rz_io_desc_size(desc);
|
|
// second map
|
|
if (size && ((UT64_MAX - size + 1) < at)) {
|
|
// split map into 2 maps if only 1 big map results into interger overflow
|
|
io_map_new(io, desc->fd, desc->perm, UT64_MAX - at + 1, 0LL, size - (UT64_MAX - at) - 1);
|
|
// someone pls take a look at this confusing stuff
|
|
size = UT64_MAX - at + 1;
|
|
}
|
|
// skyline not updated
|
|
RzIOMap *m = rz_io_map_new(io, desc->fd, desc->perm, 0LL, at, size);
|
|
if (map) {
|
|
*map = m;
|
|
}
|
|
return desc;
|
|
}
|
|
|
|
/* opens many files, without mapping them. This should be discussed */
|
|
RZ_API RzList /*<RzIODesc *>*/ *rz_io_open_many(RzIO *io, const char *uri, int perm, int mode) {
|
|
RzList *desc_list;
|
|
RzListIter *iter;
|
|
RzIODesc *desc;
|
|
rz_return_val_if_fail(io && io->files && uri, NULL);
|
|
RzIOPlugin *plugin = rz_io_plugin_resolve(io, uri, 1);
|
|
if (!plugin || !plugin->open_many || !plugin->close) {
|
|
return NULL;
|
|
}
|
|
if (!(desc_list = plugin->open_many(io, uri, perm, mode))) {
|
|
return NULL;
|
|
}
|
|
rz_list_foreach (desc_list, iter, desc) {
|
|
if (desc) {
|
|
if (!desc->plugin) {
|
|
desc->plugin = plugin;
|
|
}
|
|
if (!desc->uri) {
|
|
desc->uri = rz_str_dup(uri);
|
|
}
|
|
// should autofd be honored here?
|
|
rz_io_desc_add(io, desc);
|
|
if (!io->desc) {
|
|
io->desc = desc;
|
|
}
|
|
}
|
|
}
|
|
return desc_list;
|
|
}
|
|
|
|
RZ_API bool rz_io_reopen(RzIO *io, int fd, int perm, int mode) {
|
|
RzIODesc *old, *new;
|
|
char *uri;
|
|
if (!(old = rz_io_desc_get(io, fd))) {
|
|
return false;
|
|
}
|
|
// does this really work, or do we have to handler debuggers ugly
|
|
uri = old->referer ? old->referer : old->uri;
|
|
#if __WINDOWS__ // TODO: workaround, see https://github.com/rizinorg/rizin/issues/8840
|
|
if (old->plugin->close && old->plugin->close(old)) {
|
|
return false; // TODO: this is an unrecoverable scenario
|
|
}
|
|
if (!(new = rz_io_open_nomap(io, uri, perm, mode))) {
|
|
return false;
|
|
}
|
|
rz_io_desc_exchange(io, old->fd, new->fd);
|
|
rz_io_desc_del(io, old->fd);
|
|
return true;
|
|
#else
|
|
if (!(new = rz_io_open_nomap(io, uri, perm, mode))) {
|
|
return false;
|
|
}
|
|
rz_io_desc_exchange(io, old->fd, new->fd);
|
|
return rz_io_desc_close(old); // magic
|
|
#endif // __WINDOWS__
|
|
}
|
|
|
|
RZ_API int rz_io_close_all(RzIO *io) { // what about undo?
|
|
if (!io) {
|
|
return false;
|
|
}
|
|
rz_io_desc_fini(io);
|
|
rz_io_map_reset(io);
|
|
rz_io_desc_init(io);
|
|
rz_io_cache_fini(io);
|
|
return true;
|
|
}
|
|
|
|
RZ_API int rz_io_pread_at(RzIO *io, ut64 paddr, ut8 *buf, size_t len) {
|
|
rz_return_val_if_fail(io && buf && len >= 0, -1);
|
|
if (io->ff) {
|
|
memset(buf, io->Oxff, len);
|
|
}
|
|
return rz_io_desc_read_at(io->desc, paddr, buf, len);
|
|
}
|
|
|
|
RZ_API int rz_io_pwrite_at(RzIO *io, ut64 paddr, const ut8 *buf, size_t len) {
|
|
rz_return_val_if_fail(io && buf && len > 0, -1);
|
|
return rz_io_desc_write_at(io->desc, paddr, buf, len);
|
|
}
|
|
|
|
// Returns true if all reads on mapped regions are successful and complete.
|
|
RZ_API bool rz_io_vread_at_mapped(RzIO *io, ut64 vaddr, ut8 *buf, size_t len) {
|
|
rz_return_val_if_fail(io && buf && len > 0, false);
|
|
if (io->ff) {
|
|
memset(buf, io->Oxff, len);
|
|
}
|
|
return on_map_skyline(io, vaddr, buf, len, RZ_PERM_R, fd_read_at_wrap, false) > 0;
|
|
}
|
|
|
|
static bool rz_io_vwrite_at(RzIO *io, ut64 vaddr, const ut8 *buf, size_t len) {
|
|
return on_map_skyline(io, vaddr, (ut8 *)buf, len, RZ_PERM_W, fd_write_at_wrap, false);
|
|
}
|
|
|
|
/**
|
|
* \brief Read a chunk of memory from io
|
|
*
|
|
* Reads from io depending on io->va. Unmapped regions are filled with io->Oxff
|
|
* in both physical and virtual modes. Use this function if you want to ignore
|
|
* gaps or do not care about the number of bytes read from actually mapped
|
|
* regions.
|
|
*
|
|
* \param addr address to start reading at
|
|
* \param len size of \p buf
|
|
* \return true iff all reads on mapped regions are successful and complete
|
|
*/
|
|
RZ_API bool rz_io_read_at_mapped(RZ_NONNULL RzIO *io, ut64 addr, RZ_OUT RZ_NONNULL ut8 *buf, size_t len) {
|
|
rz_return_val_if_fail(io && buf, false);
|
|
if (len < 1) {
|
|
return false;
|
|
}
|
|
|
|
bool ret = false;
|
|
if (io->ff) {
|
|
memset(buf, io->Oxff, len);
|
|
}
|
|
if (io->va) {
|
|
ret = on_map_skyline(io, addr, buf, len, RZ_PERM_R, fd_read_at_wrap, false) > 0;
|
|
} else {
|
|
ret = rz_io_pread_at(io, addr, buf, len) > 0;
|
|
}
|
|
if (io->cached & RZ_PERM_R) {
|
|
ret |= rz_io_cache_read(io, addr, buf, len, NULL);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* \brief Read a prefix until the next mapping boundary
|
|
*
|
|
* Reads from io depending on io->va. Similar to rz_io_read_at_mapped, but for
|
|
* virtual addressing, stops as soon as a an unmapped byte is encountered.
|
|
*
|
|
* \param addr address to start reading at
|
|
* \param len size of \p buf
|
|
* \return The number of bytes read or -1 on error
|
|
*
|
|
* NOTE: If IO cache is enabled this function might read beyond a memory map.
|
|
* If the cache holds data beyond but adjacent to the map, and addr + len covers
|
|
* it, the function might read this cached data.
|
|
*/
|
|
RZ_API int rz_io_nread_at(RZ_NONNULL RzIO *io, ut64 addr, RZ_OUT RZ_NONNULL ut8 *buf, size_t len) {
|
|
int ret;
|
|
rz_return_val_if_fail(io && buf && len >= 0, -1);
|
|
if (len == 0) {
|
|
return 0;
|
|
}
|
|
if (io->va) {
|
|
if (io->ff) {
|
|
memset(buf, io->Oxff, len);
|
|
}
|
|
ret = on_map_skyline(io, addr, buf, len, RZ_PERM_R, fd_read_at_wrap, true);
|
|
} else {
|
|
ret = rz_io_pread_at(io, addr, buf, len);
|
|
}
|
|
if (ret > 0 && io->cached & RZ_PERM_R) {
|
|
// rz_io_cache_read() reads over holes in its cache.
|
|
// Because of that, it can read beyond the mapped region.
|
|
// But cache read within boundaries is simply not possible with the
|
|
// current implementation.
|
|
rz_io_cache_read(io, addr, buf, ret, NULL);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* \brief Reads from IO and returns the result in a RzBuffer.
|
|
* The actual number of bytes read, should be checked with rz_buf_size()
|
|
* on the returned buffer.
|
|
*
|
|
* \param io The IO object to read from.
|
|
* \param addr The address to read data from.
|
|
* \param len The number of bytes to read. It must be >0.
|
|
*
|
|
* \return The buffer holding the read bytes or NULL in case of failure.
|
|
*/
|
|
RZ_API RZ_OWN RzBuffer *rz_io_nread_at_new_buf(RZ_NONNULL RzIO *io, ut64 addr, size_t len) {
|
|
rz_return_val_if_fail(io && len > 0, NULL);
|
|
|
|
ut8 *raw_buf = malloc(len);
|
|
int ret = rz_io_nread_at(io, addr, raw_buf, len);
|
|
if (ret <= 0) {
|
|
RZ_LOG_ERROR("Failed to read from IO.\n");
|
|
free(raw_buf);
|
|
return NULL;
|
|
}
|
|
RzBuffer *buf = rz_buf_new_from_bytes(raw_buf, len);
|
|
if (!buf) {
|
|
RZ_LOG_ERROR("Failed to initialize RzBuffer.\n");
|
|
free(raw_buf);
|
|
return NULL;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* \brief Writes \p len bytes of data from \p buf to \p addr into the given \p io.
|
|
*
|
|
* \param io The IO object to write to.
|
|
* \param addr The address the data is written.
|
|
* \param buf The buffer to read the data for writing.
|
|
* \param len The number of bytes to write.
|
|
* \return true The write was successful.
|
|
* \return false The write as unsucessful or \p len was less then 1.
|
|
*/
|
|
RZ_API bool rz_io_write_at(RzIO *io, ut64 addr, const ut8 *buf, size_t len) {
|
|
int i;
|
|
bool ret = false;
|
|
ut8 *mybuf = (ut8 *)buf;
|
|
rz_return_val_if_fail(io && buf && len > 0, false);
|
|
if (io->write_mask) {
|
|
mybuf = rz_mem_dup((void *)buf, len);
|
|
for (i = 0; i < len; i++) {
|
|
// TODO: this needs some love because it is not optimal.
|
|
mybuf[i] &= io->write_mask[i % io->write_mask_len];
|
|
}
|
|
}
|
|
if (io->cached & RZ_PERM_W) {
|
|
ret = rz_io_cache_write(io, addr, mybuf, len);
|
|
} else if (io->va) {
|
|
ret = rz_io_vwrite_at(io, addr, mybuf, len);
|
|
} else {
|
|
ret = rz_io_pwrite_at(io, addr, mybuf, len) > 0;
|
|
}
|
|
if (buf != mybuf) {
|
|
free(mybuf);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
RZ_API bool rz_io_read(RzIO *io, ut8 *buf, size_t len) {
|
|
if (io && rz_io_read_at_mapped(io, io->off, buf, len)) {
|
|
io->off += len;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API bool rz_io_write(RzIO *io, const ut8 *buf, size_t len) {
|
|
if (io && buf && len > 0 && rz_io_write_at(io, io->off, buf, len)) {
|
|
io->off += len;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API ut64 rz_io_size(RzIO *io) {
|
|
// TODO: rethink this, maybe not needed
|
|
return io ? rz_io_desc_size(io->desc) : 0LL;
|
|
}
|
|
|
|
RZ_API bool rz_io_is_listener(RzIO *io) {
|
|
if (io && io->desc && io->desc->plugin && io->desc->plugin->listener) {
|
|
return io->desc->plugin->listener(io->desc);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API char *rz_io_system(RzIO *io, const char *cmd) {
|
|
if (io && io->desc && io->desc->plugin && io->desc->plugin->system && RZ_STR_ISNOTEMPTY(cmd)) {
|
|
return io->desc->plugin->system(io, io->desc, cmd);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API bool rz_io_resize(RZ_NONNULL RzIO *io, ut64 newsize) {
|
|
rz_return_val_if_fail(io && io->desc, false);
|
|
|
|
RzList *maps = rz_io_map_get_for_fd(io, io->desc->fd);
|
|
ut64 fd_size = rz_io_fd_size(io, io->desc->fd);
|
|
if (!rz_io_desc_resize(io->desc, newsize)) {
|
|
rz_list_free(maps);
|
|
return false;
|
|
}
|
|
RzListIter *iter;
|
|
RzIOMap *current_map;
|
|
rz_list_foreach (maps, iter, current_map) {
|
|
// we just resize map of the same size of its fd
|
|
if (current_map->itv.size == fd_size) {
|
|
rz_io_map_resize(io, current_map->id, newsize);
|
|
}
|
|
}
|
|
rz_list_free(maps);
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_io_close(RzIO *io) {
|
|
return io ? rz_io_desc_close(io->desc) : false;
|
|
}
|
|
|
|
/**
|
|
* \brief Extend the RzIODesc at \p addr by inserting \p size 0 bytes
|
|
*
|
|
* \param io Reference to RzIO instance
|
|
* \param addr Address where to insert new 0 bytes
|
|
* \param size Number of 0 bytes to insert
|
|
* \return true if extend operation was successful, false otherwise
|
|
*/
|
|
RZ_API bool rz_io_extend_at(RzIO *io, ut64 addr, ut64 size) {
|
|
#define IO_EXTEND_BLOCK_SZ 256
|
|
rz_return_val_if_fail(io, false);
|
|
|
|
if (!io->desc || !io->desc->plugin) {
|
|
return false;
|
|
}
|
|
if (size == 0) {
|
|
return true;
|
|
}
|
|
|
|
if ((io->desc->perm & RZ_PERM_RW) != RZ_PERM_RW) {
|
|
return false;
|
|
}
|
|
ut64 cur_size = rz_io_desc_size(io->desc);
|
|
if (addr > cur_size) {
|
|
return false;
|
|
}
|
|
|
|
// Extend the file to include the additional <size> bytes
|
|
if (UT64_ADD_OVFCHK(cur_size, size)) {
|
|
return false;
|
|
}
|
|
if (!rz_io_resize(io, cur_size + size)) {
|
|
return false;
|
|
}
|
|
|
|
// Shift old data to make space for the zero bytes
|
|
ut64 tmp = cur_size >= IO_EXTEND_BLOCK_SZ ? RZ_MAX(cur_size - IO_EXTEND_BLOCK_SZ, addr) : addr;
|
|
ut64 remaining = cur_size - addr;
|
|
|
|
ut8 *buffer = RZ_NEWS(ut8, IO_EXTEND_BLOCK_SZ);
|
|
if (!buffer) {
|
|
return false;
|
|
}
|
|
while (remaining) {
|
|
int sz = rz_io_pread_at(io, tmp, buffer, IO_EXTEND_BLOCK_SZ);
|
|
rz_io_pwrite_at(io, tmp + size, buffer, sz);
|
|
|
|
tmp = tmp - IO_EXTEND_BLOCK_SZ > addr ? tmp - IO_EXTEND_BLOCK_SZ : addr;
|
|
remaining = remaining > sz ? remaining - sz : 0;
|
|
}
|
|
free(buffer);
|
|
|
|
// Put the zero bytes at the right place
|
|
ut8 *empty = RZ_NEWS0(ut8, size);
|
|
if (!empty) {
|
|
return false;
|
|
}
|
|
rz_io_pwrite_at(io, addr, empty, size);
|
|
free(empty);
|
|
|
|
return true;
|
|
#undef IO_EXTEND_BLOCK_SZ
|
|
}
|
|
|
|
/**
|
|
* \brief Set a mask that is used on all following write operations
|
|
*
|
|
* \param io Reference to RzIo instance
|
|
* \param mask Mask to apply
|
|
* \param len Number of bytes in the mask
|
|
* \return true if the mask was correctly set, false otherwise
|
|
*/
|
|
RZ_API bool rz_io_set_write_mask(RzIO *io, const ut8 *mask, size_t len) {
|
|
rz_return_val_if_fail(io, false);
|
|
rz_return_val_if_fail(mask || len == 0, false);
|
|
|
|
free(io->write_mask);
|
|
if (!mask) {
|
|
io->write_mask = NULL;
|
|
io->write_mask_len = 0;
|
|
return true;
|
|
}
|
|
io->write_mask = (ut8 *)malloc(len);
|
|
memcpy(io->write_mask, mask, len);
|
|
io->write_mask_len = len;
|
|
return true;
|
|
}
|
|
|
|
RZ_API ut64 rz_io_p2v(RzIO *io, ut64 pa) {
|
|
RzIOMap *map = rz_io_map_get_paddr(io, pa);
|
|
if (map) {
|
|
return pa - map->delta + map->itv.addr;
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
RZ_API ut64 rz_io_v2p(RzIO *io, ut64 va) {
|
|
RzIOMap *map = rz_io_map_get(io, va);
|
|
if (map) {
|
|
st64 delta = va - map->itv.addr;
|
|
return map->delta + delta;
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
RZ_API void rz_io_bind(RzIO *io, RzIOBind *bnd) {
|
|
rz_return_if_fail(io && bnd);
|
|
|
|
bnd->io = io;
|
|
bnd->init = true;
|
|
bnd->fd_get_current = rz_io_fd_get_current;
|
|
bnd->desc_use = rz_io_use_fd;
|
|
bnd->desc_get = rz_io_desc_get;
|
|
bnd->desc_size = rz_io_desc_size;
|
|
bnd->p2v = rz_io_p2v;
|
|
bnd->v2p = rz_io_v2p;
|
|
bnd->open = rz_io_open_nomap;
|
|
bnd->open_at = rz_io_open_at;
|
|
bnd->close = rz_io_fd_close;
|
|
bnd->read_at = rz_io_read_at_mapped;
|
|
bnd->write_at = rz_io_write_at;
|
|
bnd->system = rz_io_system;
|
|
bnd->fd_open = rz_io_fd_open;
|
|
bnd->fd_close = rz_io_fd_close;
|
|
bnd->fd_seek = rz_io_fd_seek;
|
|
bnd->fd_size = rz_io_fd_size;
|
|
bnd->fd_resize = rz_io_fd_resize;
|
|
bnd->fd_read = rz_io_fd_read;
|
|
bnd->fd_write = rz_io_fd_write;
|
|
bnd->fd_read_at = rz_io_fd_read_at;
|
|
bnd->fd_write_at = rz_io_fd_write_at;
|
|
bnd->fd_is_dbg = rz_io_fd_is_dbg;
|
|
bnd->fd_get_name = rz_io_fd_get_name;
|
|
bnd->fd_get_map = rz_io_map_get_for_fd;
|
|
bnd->fd_remap = rz_io_map_remap_fd;
|
|
bnd->fd_getbuf = rz_io_fd_get_buf;
|
|
bnd->is_valid_offset = rz_io_is_valid_offset;
|
|
bnd->map_get = rz_io_map_get;
|
|
bnd->map_get_paddr = rz_io_map_get_paddr;
|
|
bnd->addr_is_mapped = rz_io_addr_is_mapped;
|
|
bnd->map_add = rz_io_map_add;
|
|
#if HAVE_PTRACE
|
|
bnd->ptrace = rz_io_ptrace;
|
|
bnd->ptrace_func = rz_io_ptrace_func;
|
|
#endif
|
|
#if __WINDOWS__
|
|
bnd->get_w32dbg_wrap = rz_io_get_w32dbg_wrap;
|
|
#endif
|
|
}
|
|
|
|
/* moves bytes up (+) or down (-) within the specified range */
|
|
RZ_API bool rz_io_shift(RzIO *io, ut64 start, ut64 end, st64 move) {
|
|
ut8 *buf;
|
|
ut64 chunksize = 0x10000;
|
|
ut64 saved_off = io->off;
|
|
ut64 src, shiftsize = rz_num_abs(move);
|
|
if (!shiftsize || (end - start) <= shiftsize) {
|
|
return false;
|
|
}
|
|
ut64 rest = (end - start) - shiftsize;
|
|
if (!(buf = calloc(1, chunksize + 1))) {
|
|
return false;
|
|
}
|
|
if (move > 0) {
|
|
src = end - shiftsize;
|
|
} else {
|
|
src = start + shiftsize;
|
|
}
|
|
while (rest > 0) {
|
|
if (chunksize > rest) {
|
|
chunksize = rest;
|
|
}
|
|
if (move > 0) {
|
|
src -= chunksize;
|
|
}
|
|
rz_io_read_at_mapped(io, src, buf, chunksize);
|
|
rz_io_write_at(io, src + move, buf, chunksize);
|
|
if (move < 0) {
|
|
src += chunksize;
|
|
}
|
|
rest -= chunksize;
|
|
}
|
|
free(buf);
|
|
io->off = rz_io_desc_seek(io->desc, saved_off, RZ_IO_SEEK_SET);
|
|
return true;
|
|
}
|
|
|
|
RZ_API ut64 rz_io_seek(RzIO *io, ut64 offset, int whence) {
|
|
if (!io) {
|
|
return 0LL;
|
|
}
|
|
switch (whence) {
|
|
case RZ_IO_SEEK_SET:
|
|
io->off = offset;
|
|
break;
|
|
case RZ_IO_SEEK_CUR:
|
|
io->off += offset;
|
|
break;
|
|
case RZ_IO_SEEK_END:
|
|
default:
|
|
io->off = rz_io_desc_seek(io->desc, offset, whence);
|
|
break;
|
|
}
|
|
return io->off;
|
|
}
|
|
|
|
#if HAVE_PTRACE
|
|
|
|
#if USE_PTRACE_WRAP
|
|
#include <ptrace_wrap.h>
|
|
#include <errno.h>
|
|
|
|
static ptrace_wrap_instance *io_ptrace_wrap_instance(RzIO *io) {
|
|
if (!io->ptrace_wrap) {
|
|
io->ptrace_wrap = RZ_NEW(ptrace_wrap_instance);
|
|
if (!io->ptrace_wrap) {
|
|
return NULL;
|
|
}
|
|
if (ptrace_wrap_instance_start(io->ptrace_wrap) < 0) {
|
|
RZ_FREE(io->ptrace_wrap);
|
|
return NULL;
|
|
}
|
|
}
|
|
return io->ptrace_wrap;
|
|
}
|
|
#endif
|
|
|
|
RZ_API long rz_io_ptrace(RzIO *io, rz_ptrace_request_t request, pid_t pid, void *addr, rz_ptrace_data_t data) {
|
|
#if USE_PTRACE_WRAP
|
|
ptrace_wrap_instance *wrap = io_ptrace_wrap_instance(io);
|
|
if (!wrap) {
|
|
errno = 0;
|
|
return -1;
|
|
}
|
|
return ptrace_wrap(wrap, request, pid, addr, data);
|
|
#else
|
|
return ptrace(request, pid, addr, data);
|
|
#endif
|
|
}
|
|
|
|
RZ_API pid_t rz_io_ptrace_fork(RzIO *io, void (*child_callback)(void *), void *child_callback_user) {
|
|
#if USE_PTRACE_WRAP
|
|
ptrace_wrap_instance *wrap = io_ptrace_wrap_instance(io);
|
|
if (!wrap) {
|
|
errno = 0;
|
|
return -1;
|
|
}
|
|
return ptrace_wrap_fork(wrap, child_callback, child_callback_user);
|
|
#else
|
|
pid_t r = rz_sys_fork();
|
|
if (r == 0) {
|
|
child_callback(child_callback_user);
|
|
}
|
|
return r;
|
|
#endif
|
|
}
|
|
|
|
RZ_API void *rz_io_ptrace_func(RzIO *io, void *(*func)(void *), void *user) {
|
|
#if USE_PTRACE_WRAP
|
|
ptrace_wrap_instance *wrap = io_ptrace_wrap_instance(io);
|
|
if (wrap) {
|
|
return ptrace_wrap_func(wrap, func, user);
|
|
}
|
|
#endif
|
|
return func(user);
|
|
}
|
|
#endif
|
|
|
|
#if __WINDOWS__
|
|
/// Lazily initializing getter for the w32dbg_wrap instance. Only use this and don't access io->w32dbg_wrap directly.
|
|
RZ_API struct w32dbg_wrap_instance_t *rz_io_get_w32dbg_wrap(RzIO *io) {
|
|
if (!io->priv_w32dbg_wrap) {
|
|
io->priv_w32dbg_wrap = (struct w32dbg_wrap_instance_t *)w32dbg_wrap_new();
|
|
}
|
|
return io->priv_w32dbg_wrap;
|
|
}
|
|
#endif
|
|
|
|
// remove all descs and maps
|
|
RZ_API int rz_io_fini(RzIO *io) {
|
|
if (!io) {
|
|
return false;
|
|
}
|
|
rz_io_desc_cache_fini_all(io);
|
|
rz_io_desc_fini(io);
|
|
rz_io_map_fini(io);
|
|
ht_sp_free(io->plugins);
|
|
rz_io_cache_fini(io);
|
|
if (io->runprofile) {
|
|
RZ_FREE(io->runprofile);
|
|
}
|
|
rz_event_free(io->event);
|
|
free(io->envprofile);
|
|
#if RZ_IO_USE_PTRACE_WRAP
|
|
if (io->ptrace_wrap) {
|
|
ptrace_wrap_instance_stop(io->ptrace_wrap);
|
|
free(io->ptrace_wrap);
|
|
}
|
|
#endif
|
|
#if __WINDOWS__
|
|
w32dbg_wrap_free((W32DbgWInst *)io->priv_w32dbg_wrap);
|
|
#endif
|
|
return true;
|
|
}
|