mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-30 16:40:57 +00:00
775 lines
18 KiB
C
775 lines
18 KiB
C
/* radare - LGPL - Copyright 2009-2024 - ret2libc, pancake */
|
|
|
|
#include <r_types.h>
|
|
#include <r_util.h>
|
|
#include <r_io.h>
|
|
|
|
typedef enum {
|
|
R_BUFFER_FILE,
|
|
R_BUFFER_IO,
|
|
R_BUFFER_BYTES,
|
|
R_BUFFER_MMAP,
|
|
R_BUFFER_SPARSE,
|
|
R_BUFFER_REF,
|
|
#if R2_USE_NEW_ABI
|
|
R_BUFFER_CACHE,
|
|
#endif
|
|
} RBufferType;
|
|
|
|
#include "buf_file.c"
|
|
#include "buf_sparse.c"
|
|
#include "buf_bytes.c"
|
|
#include "buf_mmap.c"
|
|
#include "buf_io.c"
|
|
#include "buf_ref.c"
|
|
|
|
#if R2_USE_NEW_ABI
|
|
#include "buf_cache.c"
|
|
#endif
|
|
|
|
static bool buf_init(RBuffer *b, const void *user) {
|
|
R_RETURN_VAL_IF_FAIL (b && b->methods, false);
|
|
const RBufferInit init = b->methods->init;
|
|
return init? init (b, user): true;
|
|
}
|
|
|
|
static void buf_wholefree(RBuffer *b) {
|
|
if (!b->methods->get_whole_buf) {
|
|
R_FREE (b->whole_buf);
|
|
}
|
|
}
|
|
|
|
static bool buf_fini(RBuffer *b) {
|
|
R_RETURN_VAL_IF_FAIL (b && b->methods, false);
|
|
const RBufferFini fini = b->methods->fini;
|
|
return fini? fini (b): true;
|
|
}
|
|
|
|
static ut64 buf_get_size(RBuffer *b) {
|
|
R_RETURN_VAL_IF_FAIL (b && b->methods, UT64_MAX);
|
|
const RBufferGetSize get_size = b->methods->get_size;
|
|
return get_size? get_size (b): UT64_MAX;
|
|
}
|
|
|
|
static st64 buf_read(RBuffer *b, ut8 *buf, size_t len) {
|
|
R_RETURN_VAL_IF_FAIL (b && b->methods, -1);
|
|
const RBufferRead bufread = b->methods->read;
|
|
R_RETURN_VAL_IF_FAIL (bufread, -1);
|
|
return bufread (b, buf, len);
|
|
}
|
|
|
|
static st64 buf_write(RBuffer *b, const ut8 *buf, size_t len) {
|
|
R_RETURN_VAL_IF_FAIL (b && b->methods, -1);
|
|
buf_wholefree (b);
|
|
const RBufferWrite bufwrite = b->methods->write;
|
|
R_RETURN_VAL_IF_FAIL (bufwrite, -1);
|
|
return bufwrite (b, buf, len);
|
|
}
|
|
|
|
static st64 buf_seek(RBuffer *b, st64 addr, int whence) {
|
|
R_RETURN_VAL_IF_FAIL (b && b->methods, -1);
|
|
const RBufferSeek bufseek = b->methods->seek;
|
|
R_RETURN_VAL_IF_FAIL (bufseek, -1);
|
|
return bufseek (b, addr, whence);
|
|
}
|
|
|
|
static bool buf_resize(RBuffer *b, ut64 newsize) {
|
|
R_RETURN_VAL_IF_FAIL (b && b->methods, -1);
|
|
const RBufferResize bufresize = b->methods->resize;
|
|
R_RETURN_VAL_IF_FAIL (bufresize, false);
|
|
return bufresize (b, newsize);
|
|
}
|
|
|
|
static ut8 *get_whole_buf(RBuffer *b, ut64 *sz) {
|
|
R_RETURN_VAL_IF_FAIL (b && b->methods, NULL);
|
|
RBufferGetWholeBuf bufwhole = b->methods->get_whole_buf;
|
|
if (bufwhole) {
|
|
return bufwhole (b, sz);
|
|
}
|
|
ut64 bsz = r_buf_size (b);
|
|
// bsz = 4096; // FAKE MINIMUM SIZE TO READ THE BIN HEADER
|
|
if (bsz == UT64_MAX) {
|
|
return NULL;
|
|
}
|
|
free (b->whole_buf);
|
|
b->whole_buf = R_NEWS (ut8, bsz);
|
|
if (!b->whole_buf) {
|
|
return NULL;
|
|
}
|
|
r_buf_read_at (b, 0, b->whole_buf, bsz);
|
|
if (sz) {
|
|
*sz = bsz;
|
|
}
|
|
return b->whole_buf;
|
|
}
|
|
|
|
static RBuffer *new_buffer(RBufferType type, const void *user) {
|
|
RBuffer *b = R_NEW0 (RBuffer);
|
|
if (!b) {
|
|
return NULL;
|
|
}
|
|
switch (type) {
|
|
case R_BUFFER_BYTES:
|
|
b->methods = &buffer_bytes_methods;
|
|
break;
|
|
case R_BUFFER_MMAP:
|
|
b->methods = &buffer_mmap_methods;
|
|
break;
|
|
#if R2_USE_NEW_ABI
|
|
case R_BUFFER_CACHE:
|
|
b->methods = &buffer_cache_methods;
|
|
break;
|
|
#endif
|
|
case R_BUFFER_SPARSE:
|
|
b->methods = &buffer_sparse_methods;
|
|
break;
|
|
case R_BUFFER_FILE:
|
|
b->methods = &buffer_file_methods;
|
|
break;
|
|
case R_BUFFER_IO:
|
|
b->methods = &buffer_io_methods;
|
|
break;
|
|
case R_BUFFER_REF:
|
|
b->methods = &buffer_ref_methods;
|
|
break;
|
|
default:
|
|
r_warn_if_reached ();
|
|
break;
|
|
}
|
|
if (!buf_init (b, user)) {
|
|
free (b);
|
|
return NULL;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
// TODO: Optimize to use memcpy when buffers are not in range..
|
|
// check buf boundaries and offsets and use memcpy or memmove
|
|
|
|
// copied from libr/io/cache.c:r_io_cache_read
|
|
// ret # of bytes copied
|
|
R_API RBuffer *r_buf_new_with_io(void *iob, int fd) {
|
|
R_RETURN_VAL_IF_FAIL (iob && fd >= 0, NULL);
|
|
struct buf_io_user u = {0};
|
|
u.iob = (RIOBind *)iob;
|
|
u.fd = fd;
|
|
return new_buffer (R_BUFFER_IO, &u);
|
|
}
|
|
|
|
R_API RBuffer *r_buf_new_with_pointers(const ut8 *bytes, ut64 len, bool steal) {
|
|
struct buf_bytes_user u = {0};
|
|
u.data_steal = bytes;
|
|
u.length = len;
|
|
u.steal = steal;
|
|
return new_buffer (R_BUFFER_BYTES, &u);
|
|
}
|
|
|
|
R_API RBuffer *r_buf_new_empty(ut64 len) {
|
|
ut8 *buf = R_NEWS0 (ut8, len);
|
|
if (!buf) {
|
|
return NULL;
|
|
}
|
|
|
|
struct buf_bytes_user u = {0};
|
|
u.data_steal = buf;
|
|
u.length = len;
|
|
u.steal = true;
|
|
RBuffer *res = new_buffer (R_BUFFER_BYTES, &u);
|
|
if (!res) {
|
|
free (buf);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
R_API RBuffer *r_buf_new_with_bytes(const ut8 *bytes, ut64 len) {
|
|
struct buf_bytes_user u = {0};
|
|
u.data = bytes;
|
|
u.length = len;
|
|
return new_buffer (R_BUFFER_BYTES, &u);
|
|
}
|
|
|
|
R_API RBuffer *r_buf_new_slice(RBuffer *b, ut64 offset, ut64 size) {
|
|
struct buf_ref_user u = {0};
|
|
u.parent = b;
|
|
u.offset = offset;
|
|
u.size = size;
|
|
return new_buffer (R_BUFFER_REF, &u);
|
|
}
|
|
|
|
R_API RBuffer *r_buf_new_with_string(const char *msg) {
|
|
return r_buf_new_with_bytes ((const ut8 *)msg, (ut64)strlen (msg));
|
|
}
|
|
|
|
R_API RBuffer *r_buf_new_with_buf(RBuffer *b) {
|
|
ut64 sz = 0;
|
|
const ut8 *tmp = r_buf_data (b, &sz);
|
|
return r_buf_new_with_bytes (tmp, sz);
|
|
}
|
|
|
|
R_API RBuffer *r_buf_new_sparse(ut8 Oxff) {
|
|
RBuffer *b = new_buffer (R_BUFFER_SPARSE, NULL);
|
|
if (b) {
|
|
b->Oxff_priv = Oxff;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
#if R2_USE_NEW_ABI
|
|
R_API RBuffer *r_buf_new_with_cache(RBuffer *sb, bool steal) {
|
|
RBuffer *b = new_buffer (R_BUFFER_CACHE, NULL);
|
|
if (b) {
|
|
struct minicachebuf {
|
|
RBuffer *sb;
|
|
bool owned;
|
|
ut64 length;
|
|
};
|
|
struct minicachebuf *mcb = b->priv;
|
|
mcb->sb = sb;
|
|
mcb->owned = steal;
|
|
mcb->length = r_buf_size (sb);
|
|
}
|
|
return b;
|
|
}
|
|
#endif
|
|
|
|
R_API RBuffer *r_buf_new(void) {
|
|
struct buf_bytes_user u = {0};
|
|
u.data = NULL;
|
|
u.length = 0;
|
|
return new_buffer (R_BUFFER_BYTES, &u);
|
|
}
|
|
|
|
R_DEPRECATE R_API const ut8 *r_buf_data(RBuffer *b, ut64 *size) {
|
|
R_RETURN_VAL_IF_FAIL (b, NULL);
|
|
b->whole_buf = get_whole_buf (b, size);
|
|
return b->whole_buf;
|
|
}
|
|
|
|
R_API ut64 r_buf_size(RBuffer *b) {
|
|
R_RETURN_VAL_IF_FAIL (b, 0);
|
|
return buf_get_size (b);
|
|
}
|
|
|
|
// rename to new?
|
|
R_API RBuffer *r_buf_new_mmap(const char *filename, int perm) {
|
|
R_RETURN_VAL_IF_FAIL (filename, NULL);
|
|
struct buf_mmap_user u = {0};
|
|
u.filename = filename;
|
|
u.perm = perm;
|
|
return new_buffer (R_BUFFER_MMAP, &u);
|
|
}
|
|
|
|
R_API RBuffer *r_buf_new_file(const char *file, int perm, int mode) {
|
|
struct buf_file_user u = {0};
|
|
u.file = file;
|
|
u.perm = perm;
|
|
u.mode = mode;
|
|
return new_buffer (R_BUFFER_FILE, &u);
|
|
}
|
|
|
|
// TODO: rename to new_from_file ?
|
|
R_API RBuffer *r_buf_new_slurp(const char *file) {
|
|
size_t len;
|
|
char *tmp = r_file_slurp (file, &len);
|
|
if (!tmp) {
|
|
return NULL;
|
|
}
|
|
|
|
struct buf_bytes_user u = {0};
|
|
u.data_steal = (ut8 *)tmp;
|
|
u.length = (ut64)len;
|
|
u.steal = true;
|
|
return new_buffer (R_BUFFER_BYTES, &u);
|
|
}
|
|
|
|
R_API bool r_buf_dump(RBuffer *b, const char *file) {
|
|
// TODO: need to redo this
|
|
if (!b || !file) {
|
|
return false;
|
|
}
|
|
ut64 tmpsz = 0;
|
|
const ut8 *tmp = r_buf_data (b, &tmpsz);
|
|
return r_file_dump (file, tmp, tmpsz, 0);
|
|
}
|
|
|
|
R_API st64 r_buf_seek(RBuffer *b, st64 addr, int whence) {
|
|
R_RETURN_VAL_IF_FAIL (b, -1);
|
|
return buf_seek (b, addr, whence);
|
|
}
|
|
|
|
R_API ut64 r_buf_tell(RBuffer *b) {
|
|
return r_buf_seek (b, 0, R_BUF_CUR);
|
|
}
|
|
|
|
R_API bool r_buf_set_bytes(RBuffer *b, const ut8 *buf, ut64 length) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf && !b->readonly, false);
|
|
if (!r_buf_resize (b, 0)) {
|
|
return false;
|
|
}
|
|
if (r_buf_seek (b, 0, R_BUF_SET) == -1) {
|
|
return false;
|
|
}
|
|
if (!r_buf_append_bytes (b, buf, length)) {
|
|
return false;
|
|
}
|
|
return r_buf_seek (b, 0, R_BUF_SET) != -1;
|
|
}
|
|
|
|
R_API bool r_buf_prepend_bytes(RBuffer *b, const ut8 *buf, ut64 length) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf && !b->readonly, false);
|
|
return r_buf_insert_bytes (b, 0, buf, length) >= 0;
|
|
}
|
|
|
|
R_API char *r_buf_tostring(RBuffer *b) {
|
|
ut64 sz = r_buf_size (b);
|
|
char *s = malloc (sz + 1);
|
|
if (!s) {
|
|
return NULL;
|
|
}
|
|
if (r_buf_read_at (b, 0, (ut8 *)s, sz) < 0) {
|
|
free (s);
|
|
return NULL;
|
|
}
|
|
s[sz] = '\0';
|
|
return s;
|
|
}
|
|
|
|
R_API bool r_buf_append_bytes(RBuffer *b, const ut8 *buf, ut64 length) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf && !b->readonly, false);
|
|
|
|
if (r_buf_seek (b, 0, R_BUF_END) == -1) {
|
|
return false;
|
|
}
|
|
return r_buf_write (b, buf, length) >= 0;
|
|
}
|
|
|
|
R_API bool r_buf_append_nbytes(RBuffer *b, ut64 length) {
|
|
R_RETURN_VAL_IF_FAIL (b && !b->readonly, false);
|
|
ut8 *buf = R_NEWS0 (ut8, length);
|
|
if (!buf) {
|
|
return false;
|
|
}
|
|
bool res = r_buf_append_bytes (b, buf, length);
|
|
free (buf);
|
|
return res;
|
|
}
|
|
|
|
R_API st64 r_buf_insert_bytes(RBuffer *b, ut64 addr, const ut8 *buf, ut64 length) {
|
|
R_RETURN_VAL_IF_FAIL (b && !b->readonly, -1);
|
|
st64 pos, r = r_buf_seek (b, 0, R_BUF_CUR);
|
|
if (r == -1) {
|
|
return r;
|
|
}
|
|
pos = r;
|
|
r = r_buf_seek (b, addr, R_BUF_SET);
|
|
if (r == -1) {
|
|
goto restore_pos;
|
|
}
|
|
|
|
ut64 sz = r_buf_size (b);
|
|
ut8 *tmp = R_NEWS (ut8, sz - addr);
|
|
r = r_buf_read (b, tmp, sz - addr);
|
|
if (r < 0) {
|
|
goto free_tmp;
|
|
}
|
|
st64 tmp_length = r;
|
|
if (!r_buf_resize (b, sz + length)) {
|
|
goto free_tmp;
|
|
}
|
|
r = r_buf_seek (b, addr + length, R_BUF_SET);
|
|
if (r == -1) {
|
|
goto free_tmp;
|
|
}
|
|
r = r_buf_write (b, tmp, tmp_length);
|
|
if (r < 0) {
|
|
goto free_tmp;
|
|
}
|
|
r = r_buf_seek (b, addr, R_BUF_SET);
|
|
if (r == -1) {
|
|
goto free_tmp;
|
|
}
|
|
r = r_buf_write (b, buf, length);
|
|
free_tmp:
|
|
free (tmp);
|
|
restore_pos:
|
|
r_buf_seek (b, pos, R_BUF_SET);
|
|
return r;
|
|
}
|
|
|
|
R_API bool r_buf_append_ut8(RBuffer *b, ut8 n) {
|
|
R_RETURN_VAL_IF_FAIL (b && !b->readonly, false);
|
|
return r_buf_append_bytes (b, (const ut8 *)&n, sizeof (n));
|
|
}
|
|
|
|
R_API bool r_buf_append_ut16(RBuffer *b, ut16 n) {
|
|
R_RETURN_VAL_IF_FAIL (b && !b->readonly, false);
|
|
return r_buf_append_bytes (b, (const ut8 *)&n, sizeof (n));
|
|
}
|
|
|
|
R_API bool r_buf_append_ut32(RBuffer *b, ut32 n) {
|
|
R_RETURN_VAL_IF_FAIL (b && !b->readonly, false);
|
|
return r_buf_append_bytes (b, (const ut8 *)&n, sizeof (n));
|
|
}
|
|
|
|
R_API bool r_buf_append_ut64(RBuffer *b, ut64 n) {
|
|
R_RETURN_VAL_IF_FAIL (b && !b->readonly, false);
|
|
return r_buf_append_bytes (b, (const ut8 *)&n, sizeof (n));
|
|
}
|
|
|
|
R_API bool r_buf_append_buf(RBuffer *b, RBuffer *a) {
|
|
R_RETURN_VAL_IF_FAIL (b && a && !b->readonly, false);
|
|
ut64 sz = 0;
|
|
const ut8 *tmp = r_buf_data (a, &sz);
|
|
return r_buf_append_bytes (b, tmp, sz);
|
|
}
|
|
|
|
R_API bool r_buf_append_buf_slice(RBuffer *b, RBuffer *a, ut64 offset, ut64 size) {
|
|
R_RETURN_VAL_IF_FAIL (b && a && !b->readonly, false);
|
|
ut8 *tmp = R_NEWS (ut8, size);
|
|
bool res = false;
|
|
if (tmp) {
|
|
st64 r = r_buf_read_at (a, offset, tmp, size);
|
|
if (r < 0) {
|
|
goto err;
|
|
}
|
|
res = r_buf_append_bytes (b, tmp, r);
|
|
err:
|
|
free (tmp);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// return an heap-allocated string read from the RBuffer b at address addr. The
|
|
// length depends on the first '\0' found in the buffer. If there is no '\0' in
|
|
// the buffer, there is no string, thus NULL is returned.
|
|
R_API char *r_buf_get_string(RBuffer *b, ut64 addr) {
|
|
const int MIN_RES_SZ = 64;
|
|
ut8 *res = R_NEWS (ut8, MIN_RES_SZ + 1);
|
|
ut64 sz = 0;
|
|
st64 r = r_buf_read_at (b, addr, res, MIN_RES_SZ);
|
|
bool null_found = false;
|
|
while (r > 0) {
|
|
const ut8 *needle = r_mem_mem (res + sz, r, (ut8 *)"\x00", 1);
|
|
if (needle) {
|
|
null_found = true;
|
|
break;
|
|
}
|
|
sz += r;
|
|
addr += r;
|
|
|
|
ut8 *restmp = realloc (res, sz + MIN_RES_SZ + 1);
|
|
if (!restmp) {
|
|
free (res);
|
|
return NULL;
|
|
}
|
|
res = restmp;
|
|
r = r_buf_read_at (b, addr, res + sz, MIN_RES_SZ);
|
|
}
|
|
if (r < 0 || !null_found) {
|
|
free (res);
|
|
return NULL;
|
|
}
|
|
return (char *)res;
|
|
}
|
|
|
|
R_API ut8 *r_buf_read_all(RBuffer *b, int *blen) {
|
|
R_RETURN_VAL_IF_FAIL (b, NULL);
|
|
int buflen = r_buf_size (b);
|
|
if (buflen < 0) {
|
|
return NULL;
|
|
}
|
|
ut8 *buf = malloc (buflen + 1);
|
|
buf_seek (b, 0, R_BUF_SET);
|
|
st64 newlen = buf_read (b, buf, buflen);
|
|
if (newlen != buflen) {
|
|
if (newlen > 0) {
|
|
buflen = newlen;
|
|
} else {
|
|
R_LOG_WARN ("buf_read_all fails");
|
|
}
|
|
}
|
|
if (blen) {
|
|
*blen = buflen;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
R_API st64 r_buf_read(RBuffer *b, ut8 *buf, ut64 len) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf, -1);
|
|
st64 r = buf_read (b, buf, len);
|
|
if (r >= 0 && r < len) {
|
|
memset (buf + r, b->Oxff_priv, len - r);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
R_API st64 r_buf_write(RBuffer *b, const ut8 *buf, ut64 len) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf && !b->readonly, -1);
|
|
return buf_write (b, buf, len);
|
|
}
|
|
|
|
R_API ut8 r_buf_read8(RBuffer *b) {
|
|
ut8 res;
|
|
st64 r = r_buf_read (b, &res, sizeof (res));
|
|
return r == sizeof (res)? res: b->Oxff_priv;
|
|
}
|
|
|
|
R_API ut8 r_buf_read8_at(RBuffer *b, ut64 addr) {
|
|
ut8 res;
|
|
st64 r = r_buf_read_at (b, addr, &res, sizeof (res));
|
|
return (r == sizeof (res))? res: b->Oxff_priv;
|
|
}
|
|
|
|
static st64 buf_format(RBuffer *dst, RBuffer *src, const char *fmt, int n) {
|
|
st64 res = 0;
|
|
int i;
|
|
for (i = 0; i < n; i++) {
|
|
int j;
|
|
int m = 1;
|
|
int tsize = 2;
|
|
bool bigendian = true;
|
|
|
|
for (j = 0; fmt[j]; j++) {
|
|
switch (fmt[j]) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
if (m == 1) {
|
|
m = r_num_get (NULL, &fmt[j]);
|
|
}
|
|
continue;
|
|
case 's': tsize = 2; bigendian = false; break;
|
|
case 'S': tsize = 2; bigendian = true; break;
|
|
case 'i': tsize = 4; bigendian = false; break;
|
|
case 'I': tsize = 4; bigendian = true; break;
|
|
case 'l': tsize = 8; bigendian = false; break;
|
|
case 'L': tsize = 8; bigendian = true; break;
|
|
case 'c': tsize = 1; bigendian = false; break;
|
|
default: return -1;
|
|
}
|
|
|
|
int k;
|
|
for (k = 0; k < m; k++) {
|
|
ut8 tmp[sizeof (ut64)];
|
|
ut8 d1;
|
|
ut16 d2;
|
|
ut32 d3;
|
|
ut64 d4;
|
|
st64 r = r_buf_read (src, tmp, tsize);
|
|
if (r != tsize) {
|
|
return -1;
|
|
}
|
|
switch (tsize) {
|
|
case 1:
|
|
d1 = r_read_ble8 (tmp);
|
|
r = r_buf_write (dst, (ut8 *)&d1, 1);
|
|
break;
|
|
case 2:
|
|
d2 = r_read_ble16 (tmp, bigendian);
|
|
r = r_buf_write (dst, (ut8 *)&d2, 2);
|
|
break;
|
|
case 4:
|
|
d3 = r_read_ble32 (tmp, bigendian);
|
|
r = r_buf_write (dst, (ut8 *)&d3, 4);
|
|
break;
|
|
case 8:
|
|
d4 = r_read_ble64 (tmp, bigendian);
|
|
r = r_buf_write (dst, (ut8 *)&d4, 8);
|
|
break;
|
|
}
|
|
if (r < 0) {
|
|
return -1;
|
|
}
|
|
res += r;
|
|
}
|
|
m = 1;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// TODO: add r_buf_fnread or nfread for safety reasons. callers never know what they are doing
|
|
R_API st64 r_buf_fread(RBuffer *b, ut8 *buf, const char *fmt, int n) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf && fmt, -1);
|
|
// XXX: we assume the caller knows what he's doing
|
|
RBuffer *dst = r_buf_new_with_pointers (buf, UT64_MAX, false);
|
|
if (dst) {
|
|
st64 res = buf_format (dst, b, fmt, n);
|
|
r_buf_free (dst);
|
|
return res;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// UNSAFE
|
|
R_API st64 r_buf_fread_at(RBuffer *b, ut64 addr, ut8 *buf, const char *fmt, int n) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf && fmt, -1);
|
|
st64 o_addr = r_buf_seek (b, 0, R_BUF_CUR);
|
|
st64 r = r_buf_seek (b, addr, R_BUF_SET);
|
|
if (r == -1) {
|
|
return r;
|
|
}
|
|
r = r_buf_fread (b, buf, fmt, n);
|
|
(void)r_buf_seek (b, o_addr, R_BUF_SET);
|
|
return r;
|
|
}
|
|
|
|
R_API st64 r_buf_fwrite(RBuffer *b, const ut8 *buf, const char *fmt, int n) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf && fmt && !b->readonly, -1);
|
|
// XXX: we assume the caller knows what he's doing
|
|
RBuffer *src = r_buf_new_with_pointers (buf, UT64_MAX, false);
|
|
st64 res = buf_format (b, src, fmt, n);
|
|
r_buf_free (src);
|
|
return res;
|
|
}
|
|
|
|
R_API st64 r_buf_fwrite_at(RBuffer *b, ut64 addr, const ut8 *buf, const char *fmt, int n) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf && fmt && !b->readonly, -1);
|
|
st64 o_addr = r_buf_seek (b, 0, R_BUF_CUR);
|
|
st64 r = r_buf_seek (b, addr, R_BUF_SET);
|
|
if (r == -1) {
|
|
return r;
|
|
}
|
|
r = r_buf_fwrite (b, buf, fmt, n);
|
|
r_buf_seek (b, o_addr, R_BUF_SET);
|
|
return r;
|
|
}
|
|
|
|
R_API st64 r_buf_read_at(RBuffer *b, ut64 addr, ut8 *buf, ut64 len) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf, -1);
|
|
st64 o_addr = r_buf_seek (b, 0, R_BUF_CUR);
|
|
st64 r = r_buf_seek (b, addr, R_BUF_SET);
|
|
if (r == -1) {
|
|
return r;
|
|
}
|
|
r = r_buf_read (b, buf, len);
|
|
r_buf_seek (b, (ut64)o_addr, R_BUF_SET);
|
|
return r;
|
|
}
|
|
|
|
R_API st64 r_buf_write_at(RBuffer *b, ut64 addr, const ut8 *buf, ut64 len) {
|
|
R_RETURN_VAL_IF_FAIL (b && buf && !b->readonly, -1);
|
|
st64 o_addr = r_buf_seek (b, 0, R_BUF_CUR);
|
|
st64 r = r_buf_seek (b, addr, R_BUF_SET);
|
|
if (r == -1) {
|
|
return r;
|
|
}
|
|
r = r_buf_write (b, buf, len);
|
|
r_buf_seek (b, o_addr, R_BUF_SET);
|
|
return r;
|
|
}
|
|
|
|
// XXX 580 use r_ref api instead
|
|
R_API void r_buf_fini(RBuffer *b) {
|
|
if (!b) {
|
|
return;
|
|
}
|
|
if (b->refctr > 0) {
|
|
b->refctr--;
|
|
return;
|
|
}
|
|
// free the whole_buf only if it was initially allocated by the buf types
|
|
if (b->methods->get_whole_buf) {
|
|
if (b->methods->free_whole_buf) {
|
|
b->methods->free_whole_buf (b);
|
|
}
|
|
} else {
|
|
buf_wholefree (b);
|
|
}
|
|
buf_fini (b);
|
|
}
|
|
|
|
// XXX 580 use r_ref api instead
|
|
R_API void r_buf_free(RBuffer *b) {
|
|
if (b) {
|
|
bool unreferenced = b && b->refctr == 0;
|
|
r_buf_fini (b);
|
|
if (unreferenced) {
|
|
free (b);
|
|
}
|
|
}
|
|
}
|
|
|
|
R_API st64 r_buf_append_string(RBuffer *b, const char *str) {
|
|
R_RETURN_VAL_IF_FAIL (b && str && !b->readonly, false);
|
|
return r_buf_append_bytes (b, (const ut8 *)str, strlen (str));
|
|
}
|
|
|
|
R_API bool r_buf_resize(RBuffer *b, ut64 newsize) {
|
|
R_RETURN_VAL_IF_FAIL (b, false);
|
|
return buf_resize (b, newsize);
|
|
}
|
|
|
|
// XXX 580 use r_ref api instead
|
|
R_API RBuffer *r_buf_ref(RBuffer *b) {
|
|
if (b) {
|
|
b->refctr++;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
R_API RList *r_buf_nonempty_list(RBuffer *b) {
|
|
const RBufferNonEmptyList nelist = b->methods->nonempty_list;
|
|
return nelist ? nelist (b): NULL;
|
|
}
|
|
|
|
R_API st64 r_buf_uleb128(RBuffer *b, ut64 *v) {
|
|
ut8 c = 0xff;
|
|
ut64 s = 0, sum = 0, l = 0;
|
|
do {
|
|
ut8 data;
|
|
st64 r = r_buf_read (b, &data, sizeof (data));
|
|
if (r < 1) {
|
|
return -1;
|
|
}
|
|
c = data & 0xff;
|
|
if (s < 64) {
|
|
sum |= ((ut64) (c & 0x7f) << s);
|
|
s += 7;
|
|
} else {
|
|
sum = 0;
|
|
}
|
|
l++;
|
|
} while (c & 0x80);
|
|
if (v) {
|
|
*v = sum;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
R_API st64 r_buf_sleb128(RBuffer *b, st64 *v) {
|
|
st64 result = 0, offset = 0;
|
|
ut8 value;
|
|
do {
|
|
st64 chunk;
|
|
st64 r = r_buf_read (b, &value, sizeof (value));
|
|
if (r != sizeof (value)) {
|
|
return -1;
|
|
}
|
|
chunk = value & 0x7f;
|
|
if (offset < 64) {
|
|
result |= (chunk << offset);
|
|
offset += 7;
|
|
} else {
|
|
result = 0;
|
|
}
|
|
} while (value & 0x80);
|
|
|
|
if ((value & 0x40) != 0) {
|
|
if (offset < 64) {
|
|
result |= ~0ULL << offset;
|
|
}
|
|
}
|
|
if (v) {
|
|
*v = result;
|
|
}
|
|
return offset / 7;
|
|
}
|