radare2/libr/io/vio.c
2016-07-03 19:32:06 +02:00

340 lines
11 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* radare - LGPL - Copyright 2014-2016 - pancake, condret */
#include <r_io.h>
#define VIO_DEBUG 0
/*
| io.va | | io.ff |
.------------------.
| |
| VADDR ---> MADDR | ---> PADDR ---> PLUGINS
| | maddr->paddr+fd
`------------------'
virtual addresses are used when io.va is enabled
and it checks for sections in order to find an address
inside a mapped range.
If a virtual address is not found in any section,
then it looks into the map addresses.
mapped addresses are used to map an RIODesc at a
specific physical address. A file can be opened, but
not mapped, in this case it will not be visible.
cache: where should it
undo api: works at vaddr level too
TODO:
- How to share data pointers?
- Load RBuffer as file?
maddr -> vaddr
it is possible to get a list of virtual addresses that
point to a given physical address from a virtual one.
*/
/* Virtual Addressing API */
#define isInMapRange(m,x) ((m->from <= x) && (x <= m->to))
/* Idea: At first, get all sections in vrange, meanwhile record all unsectioned area and store it via RIORange in a list,
read all sections via mread, resolve maps for unsectioned areas and fill the gaps. last point must always prefere using io->desc*/
R_API int r_io_vread (RIO *io, ut64 vaddr, ut8 *buf, int len) {
int tmp_len = len;
ut8 *tmp_buf = buf;
ut64 vendaddr, maddr, tmp_vaddr = vaddr;
RIOMap *map;
RIOSection *section;
RIORange *range;
RList *sections, *ranges = NULL, *maps;
RListIter *iter, *ator;
if (!io->desc) {
eprintf ("r_io_vread: desc is NULL, WTF!\n");
return R_ERROR;
}
if (len < 0) {
eprintf ("r_io_vread: wrong usage; len is smaller than 0. len: %i\n", len);
return R_FAIL;
}
sections = r_io_section_get_in_vaddr_range (io, vaddr, vaddr+len);
if (!r_list_empty (sections)) { //check if there is any section
ranges = r_list_newf (free);
if (!ranges) {
r_list_free (sections);
return false;
}
r_list_foreach (sections, iter, section) {
if (!section->vaddr) continue;
if (section->vaddr > tmp_vaddr) {
range = r_io_range_new (); //create a new range
if (!range) {
r_list_free (ranges);
r_list_free (sections);
return false;
}
range->from = tmp_vaddr; //record unsectioned area
range->to = section->vaddr;
r_list_append (ranges, range); //store the range
tmp_vaddr = section->vaddr; //prepare for resolving the maddr
tmp_len -= (tmp_vaddr - vaddr);
tmp_buf += (tmp_vaddr - vaddr); //adjust buffer
}
vendaddr = tmp_vaddr + tmp_len; //calculate the virtual end address
if (vendaddr > (section->vaddr + section->vsize)) //check if the virtual end address is in the section too
vendaddr = section->vaddr + section->vsize; //if not, size it down
maddr = tmp_vaddr - section->vaddr + section->offset; //calculate the map address (address inside the map)
if (maddr > (section->offset + section->size)) { //check if the maddr is inside the physical section, if not, skip some things
} else {
if ((vendaddr - section->vaddr + section->offset) > (section->offset + section->size)) { //check if the virtual part of the section fits into the physical part
r_io_mread (io, section->fd, maddr, tmp_buf, (section->offset + section->size) - maddr);//if not, read as far as possible
} else {
r_io_mread (io, section->fd, maddr, tmp_buf, vendaddr - tmp_vaddr); //read from the sections fd
}
}
tmp_buf += (vendaddr - tmp_vaddr); //adjust buffer
tmp_len -= (vendaddr - tmp_vaddr); //adjust length
tmp_vaddr = vendaddr; //adjust address
}
}
r_list_free (sections);
if (ranges) { //this is all might be too slow
r_list_foreach (ranges, iter, range) {
maps = r_io_map_get_maps_in_range (io, range->from, range->to - range->from); //get all maps in the range
tmp_vaddr = range->from;
tmp_len = range->to - range->from; //adjust length
tmp_buf = buf + (tmp_vaddr - vaddr); //adjust pointer
r_list_foreach (maps, ator, map) { //start filling the gaps
r_io_mread (io, map->fd, tmp_vaddr, tmp_buf, tmp_len); //read from maps, the ranges will adjusted in mread
}
r_list_free (maps); //free the list for the next iteration
r_io_mread (io, io->desc->fd, tmp_vaddr, tmp_buf, tmp_len); //ensure that io->desc is always on the top
}
r_list_free (ranges);
} else {
maps = r_io_map_get_maps_in_range (io, vaddr, vaddr + len); //get all maps
r_list_foreach (maps, iter, map) {
r_io_mread (io, map->fd, vaddr, buf, len); //read from the maps, the ranges will be adjusted in mread
}
r_list_free (maps); //free the list
r_io_mread (io, io->desc->fd, vaddr, buf, len); //ensure that io->desc is always on the top
}
return true;
}
/*you can throw any fd on this beast, it's important that len is equal or smaller than the size of buf*/
R_API int r_io_mread (RIO *io, int fd, ut64 maddr, ut8 *buf, int len) {
int read_bytes = len;
ut64 endaddr, paddr, d;
RIODesc *desc; //desc for tmp use
RIOMap *map; //map
if (len < 0) { //len must be bigger then -1
eprintf ("r_io_mread: wrong usage; len is smaller than 0. len: %i\n", len);
return R_FAIL;
}
if ((UT64_MAX - len) < maddr) { //say no to integer-overflows
eprintf ("r_io_mread: sorry, but I won't let you overflow this ut64\n");
read_bytes = UT64_MAX - maddr; //shrink len/read_bytes
}
endaddr = maddr + read_bytes; //get endaddr
map = r_io_map_resolve_in_range (io, maddr, endaddr, fd); //resolve map for fd in range
if (!map)
map = r_io_map_resolve (io, fd); //try to resolve map if it is not in range
if (!map) { //check if map exists
eprintf ("r_io_mread: cannot resolve map for fd %i\n", fd);
return R_ERROR;
}
if (endaddr > map->to) { //check if endaddr is in the map
if (maddr > map->to) //check segfault
return R_FAIL;
endaddr = map->to; //adjust endaddr
read_bytes = endaddr - maddr; //adjust read_bytes
}
if (maddr < map->from) { //adjusting things here will make vread very easy, because you can just get a list of fds in the range and the throw every fd on this function
if (endaddr < map->from) //check segfaults
return R_FAIL;
d = map->from - maddr; //get difference between maddr and start of the map
if (read_bytes < d) //check if adjusting would cause segfaults
return R_FAIL;
buf += d; //adjust buf-ptr
read_bytes -= d; //this is dangerous and can overflow
maddr += d; //adjust maddr
}
paddr = maddr - map->from + map->delta; //resolve paddr
desc = io->desc; //save io->desc
io->desc = r_io_desc_get (io, fd); //resolve desc for fd
if (!io->desc) { //check if desc exists
eprintf ("r_io_mread: cannot get desc for fd %i\n", fd);
io->desc = desc; //restore io->desc
return R_ERROR;
}
read_bytes = r_io_pread (io, paddr, buf, read_bytes); //read
io->desc = desc; //restore io->desc
return read_bytes; //return bytes read
}
R_API int r_io_pread (RIO *io, ut64 paddr, ut8 *buf, int len) {
int bytes_read = 0;
#if VIO_DEBUG
char *read_from = NULL;
#endif
if (!io) {
#if VIO_DEBUG //show debug-info
eprintf ("r_io_pread: io is NULL\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", paddr, len);
r_sys_backtrace();
#endif
return 0;
}
if (paddr == UT64_MAX) {
if (io->ff) {
memset (buf, 0xff, len);
return len;
}
return R_FAIL;
}
r_io_seek (io, paddr, R_IO_SEEK_SET);
if (io->buffer_enabled){
#if VIO_DEBUG
read_from = "buffer";
#endif
bytes_read = r_io_buffer_read (io, io->off, buf, len);
} else {
if (io->desc && io->desc->plugin && io->desc->plugin->read){
#if VIO_DEBUG
read_from = io->desc->plugin->name;
#endif
bytes_read = io->desc->plugin->read (io, io->desc, buf, len);
} else if (!io->desc) {
#if VIO_DEBUG
eprintf ("r_io_pread: io->desc is NULL\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", paddr, len);
r_sys_backtrace();
#endif
return 0;
} else {
#if VIO_DEBUG
read_from = "File";
#endif
bytes_read = read (io->desc->fd, buf, len);
}
if (bytes_read<0) {
#if VIO_DEBUG
eprintf ("r_io_pread: bytes_read %i\n"
"from: %s\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", bytes_read, read_from, paddr, len);
r_sys_backtrace();
#endif
}
}
return bytes_read;
}
// This is not so good commented, because it's mostly copy-pasta from mread
R_API int r_io_mwrite (RIO *io, int fd, ut64 maddr, ut8 *buf, int len) {
int write_bytes = len;
ut64 endaddr, paddr, d;
RIODesc *desc; //desc for tmp use
RIOMap *map; //map
if (len<0) {
eprintf ("r_io_mwrite: wrong usage; len is smaller than 0, len: %i\n", len);
return R_FAIL;
}
if ((UT64_MAX - len) < maddr) { //no overflows please
eprintf ("r_io_mwrite: no, you cannot overflow this ut64\n");
write_bytes = UT64_MAX - maddr;
}
endaddr = maddr + write_bytes;
map = r_io_map_resolve_in_range (io, maddr, endaddr, fd);
if (!map)
map = r_io_map_resolve (io, fd);
if (!map) {
eprintf ("r_io_mwrite: cannot resolve map for fd%i\n", fd);
return R_ERROR;
}
if (endaddr > map->to) {
if (maddr > map->to)
return R_FAIL;
endaddr = map->to;
write_bytes = endaddr - maddr;
}
if (maddr < map->from) {
if (endaddr < map->from)
return R_FAIL;
d = map->from - maddr;
if (write_bytes < d)
return R_FAIL;
buf += d;
write_bytes -= d;
maddr += d;
}
if (!(map->flags & R_IO_WRITE)) //check if the map allows writing
return write_bytes;
paddr = maddr - map->from + map->delta;
desc = io->desc;
io->desc = r_io_desc_get (io, fd);
if (!io->desc) {
eprintf ("r_io_mwrite: cannot get desc for fd %i\n", fd);
io->desc = desc;
return R_ERROR;
}
write_bytes = r_io_pwrite (io, paddr, buf, write_bytes);
io->desc = desc;
return write_bytes;
}
R_API int r_io_pwrite (RIO *io, ut64 paddr, const ut8 *buf, int len)
{
int bytes_written = 0;
#if VIO_DEBUG
char *written_to = NULL;
#endif
if (!io) {
#if VIO_DEBUG
eprintf ("r_io_pwrite: io is NULL\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", paddr, len);
r_sys_backtrace();
#endif
return 0;
}
if ((UT64_MAX - len) < paddr) //prevent overflows
len = UT64_MAX - paddr;
r_io_seek (io, paddr, R_IO_SEEK_SET);
if (io->desc && io->desc->plugin && io->desc->plugin->write) {
#if VIO_DEBUG
written_to = io->desc->plugin->name;
#endif
bytes_written = io->desc->plugin->write (io, io->desc, buf, len);
} else if (!io->desc) {
#if VIO_DEBUG //show debug-info
eprintf ("r_io_pwrite: io->desc is NULL\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", paddr, len);
r_sys_backtrace();
#endif
return 0;
} else {
#if VIO_DEBUG
written_to = "File";
#endif
bytes_written = write (io->desc->fd, buf, len);
}
if (bytes_written < 0) {
#if VIO_DEBUG
eprintf ("r_io_pwrite: bytes_written: %i\n"
"to: %s\n"
"paddr: 0x%016"PFMT64x"\n"
"len: 0x%x\n", bytes_written, written_to, paddr, len);
r_sys_backtrace();
#endif
}
return bytes_written;
}