radare2/libr/anal/meta.c
Ahmed Mohamed Abd El-MAwgood 13d8e65426 Enhancing comments for vars/aegs (#5127)
Adding locality properity to variable and argument comments

the old implementation made use of the standard r_meta functions to create comments using the frame offset as the address. this caused problems because more than one function might have variable at the same offset (with respect to each ones stackframe). so they will overwride each others comments.

adding `base64:` feature

prevening CC from processing var/args comment (in wrong manner)
2016-06-12 23:39:40 +02:00

625 lines
16 KiB
C

/* radare - LGPL - Copyright 2008-2015 - nibble, pancake */
// TODO: rename to r_anal_meta_get() ??
#if 0
TODO
====
- handle sync to synchronize all the data on disk.
- actually listing only works in memory
- array_add doesnt needs index, right?
- remove unused arguments from r_meta_find (where ?)
- implement r_meta_find
#endif
#if 0
SDB SPECS
DatabaseName:
'anal.meta'
Keys:
'meta.<type>.count=<int>' number of added metas where 'type' is a single char
'meta.<type>.<last>=<array>' splitted array, each block contains K elements
'meta.<type>.<addr>=<string>' string representing extra information of the meta type at given address
'range.<baddr>=<array>' store valid addresses in a base range array
#endif
#include <r_anal.h>
#include <r_print.h>
#define META_RANGE_BASE(x) ((x)>>12)
#define META_RANGE_SIZE 0xfff
#undef DB
#define DB a->sdb_meta
#if 0
// Defined but not used. Shall we remove it?
static char *meta_inrange_get (RAnal *a, ut64 addr, int size) {
char key[64];
ut64 base, base2;
base = META_RANGE_BASE (addr);
base2 = META_RANGE_BASE (addr+size);
// return string array of all the offsets where there are stuff
for (; base<base2; base += META_RANGE_SIZE) {
snprintf (key, sizeof (key)-1, "range.0x%"PFMT64x, base);
sdb_array_get (DB, key, 0, 0);
}
return NULL;
}
#endif
static int meta_inrange_add (RAnal *a, ut64 addr, int size) {
int set = 0;
char key[64];
ut64 base, base2;
base = META_RANGE_BASE (addr);
base2 = META_RANGE_BASE (addr+size);
for (; base<base2; base += META_RANGE_SIZE) {
snprintf (key, sizeof (key)-1, "range.0x%"PFMT64x, base);
if (sdb_array_add_num (DB, key, addr, 0))
set = 1;
}
return set;
}
static int meta_inrange_del (RAnal *a, ut64 addr, int size) {
int set = 0;
char key[64];
ut64 base, base2;
base = META_RANGE_BASE (addr);
base2 = META_RANGE_BASE (addr+size);
// TODO: optimize this thing?
for (; base<base2; base += META_RANGE_SIZE) {
snprintf (key, sizeof (key)-1, "range.0x%"PFMT64x, base);
if (sdb_array_remove_num (DB, key, addr, 0))
set = 1;
}
//sdb_array_del (DB);
return set;
}
// 512 = 1.5s
// 256 = 1.3s
// 128 = 1.2s
// 64 = 1.14
// 32 = 1.12
// not storing any = 1
#define K 256
static int meta_type_add (RAnal *a, char type, ut64 addr) {
char key[32];
ut32 count, last;
snprintf (key, sizeof (key)-1, "meta.%c.count", type);
count = (ut32)sdb_num_inc (DB, key, 1, 0);
last = count/K;
snprintf (key, sizeof (key)-1, "meta.%c.%d", type, last);
sdb_array_add_num (DB, key, addr, 0);
return count;
}
// TODO: Add APIs to resize meta? nope, just del and add
R_API int r_meta_set_string(RAnal *a, int type, ut64 addr, const char *s) {
char key[100], val[2048], *e_str;
int ret;
ut64 size;
int space_idx = a->meta_spaces.space_idx;
meta_type_add (a, type, addr);
snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x, type, addr);
size = sdb_array_get_num (DB, key, 0, 0);
if (!size) {
size = strlen (s);
meta_inrange_add (a, addr, size);
ret = true;
} else ret = false;
e_str = sdb_encode ((const void*)s, -1);
snprintf (val, sizeof (val)-1, "%d,%d,%s", (int)size, space_idx, e_str);
sdb_set (DB, key, val, 0);
free ((void*)e_str);
return ret;
}
R_API int r_meta_set_var_comment (RAnal *a, int type, ut64 idx, ut64 addr, const char *s) {
char key[100], val[2048], *e_str;
int ret;
ut64 size;
int space_idx = a->meta_spaces.space_idx;
meta_type_add (a, type, addr);
snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x".0x%"PFMT64x, type, addr, idx);
size = sdb_array_get_num (DB, key, 0, 0);
if (!size) {
size = strlen (s);
meta_inrange_add (a, addr, size);
ret = true;
} else ret = false;
e_str = sdb_encode ((const void*)s, -1);
snprintf (val, sizeof (val)-1, "%d,%d,%s", (int)size, space_idx, e_str);
sdb_set (DB, key, val, 0);
free ((void*)e_str);
return ret;
}
R_API char *r_meta_get_string(RAnal *a, int type, ut64 addr) {
char key[100];
const char *k, *p, *p2;
snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x, type, addr);
k = sdb_const_get (DB, key, NULL);
if (!k) return NULL;
p = strchr (k, SDB_RS);
if (!p) return NULL;
k = p+1;
p2 = strchr (k, SDB_RS);
if (!p2) {
return (char *)sdb_decode (k, NULL);
}
return (char *)sdb_decode (p2+1, NULL);
}
R_API char *r_meta_get_var_comment (RAnal *a, int type, ut64 idx, ut64 addr) {
char key[100];
const char *k, *p, *p2;
snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x".0x%"PFMT64x, type, addr, idx);
k = sdb_const_get (DB, key, NULL);
if (!k) return NULL;
p = strchr (k, SDB_RS);
if (!p) return NULL;
k = p+1;
p2 = strchr (k, SDB_RS);
if (!p2) {
return (char *)sdb_decode (k, NULL);
}
return (char *)sdb_decode (p2+1, NULL);
}
R_API int r_meta_del(RAnal *a, int type, ut64 addr, ut64 size, const char *str) {
char key[100], *dtr, *s, *p, *next;
#if 0
char key2[100];
#endif
const char *ptr;
int i;
if (size == UT64_MAX) {
// FULL CLEANUP
// XXX: this thing ignores the type
if (type == R_META_TYPE_ANY) {
sdb_reset (DB);
} else {
snprintf (key, sizeof (key)-1, "meta.%c.count", type);
int last = (ut64)sdb_num_get (DB, key, NULL)/K;
for (i=0; i<last; i++) {
snprintf (key, sizeof (key)-1, "meta.%c.%d", type, i);
dtr = sdb_get (DB, key, 0);
for (p = dtr; p; p = next) {
s = sdb_anext (p, &next);
snprintf (key, sizeof (key)-1,
"meta.%c.0x%"PFMT64x,
type, sdb_atoi (s));
eprintf ("--> %s\n", key);
sdb_unset (DB, key, 0);
if (!next) break;
}
free (dtr);
}
}
return false;
}
meta_inrange_del (a, addr, size);
snprintf (key, sizeof (key)-1, type == R_META_TYPE_COMMENT ?
"meta.C.0x%"PFMT64x : "meta.0x%"PFMT64x, addr);
ptr = sdb_const_get (DB, key, 0);
if (ptr) {
sdb_unset (DB, key, 0);
#if 0
// This code is wrong, but i guess it's necessary in case type is ANY
for (i=0; ptr[i]; i++) {
if (ptr[i] != SDB_RS) {
snprintf (key2, sizeof (key2)-1,
"meta.%c.0x%"PFMT64x, ptr[i], addr);
printf ("UNSET (%s)\n", key2);
sdb_unset (DB, key2, 0);
}
}
#endif
}
sdb_unset (DB, key, 0);
return false;
}
R_API int r_meta_var_comment_del(RAnal *a, int type, ut64 idx, ut64 addr) {
char *key;
key = r_str_newf ("meta.%c.0x%"PFMT64x"0x%"PFMT64x, type, addr, idx);
sdb_unset (DB, key, 0);
return 0;
}
R_API int r_meta_cleanup(RAnal *a, ut64 from, ut64 to) {
return r_meta_del (a, R_META_TYPE_ANY, from, (to-from), NULL);
}
R_API void r_meta_item_free(void *_item) {
RAnalMetaItem *item = _item;
free (item);
}
R_API RAnalMetaItem *r_meta_item_new(int type) {
RAnalMetaItem *mi = R_NEW0 (RAnalMetaItem);
if (mi) mi->type = type;
return mi;
}
R_API int r_meta_add(RAnal *a, int type, ut64 from, ut64 to, const char *str) {
int space_idx = a->meta_spaces.space_idx;
char *e_str, key[100], val[2048];
int exists;
if (from > to)
return false;
if (from == to)
to = from + 1;
if (type == 100 && (to-from)<1) {
return false;
}
/* set entry */
e_str = sdb_encode ((const void*)str, -1);
snprintf (key, sizeof (key)-1, "meta.%c.0x%"PFMT64x, type, from);
snprintf (val, sizeof (val)-1, "%d,%d,%s", (int)(to-from), space_idx, e_str);
exists = sdb_exists (DB, key);
sdb_set (DB, key, val, 0);
free (e_str);
// XXX: This is totally inefficient, using array_add withuot
// checking return value is wrong practice, also it may lead
// to inconsistent DB, and pretty bad performance. We should
// store this list in a different storage that doesnt have
// those limits and it's O(1) instead of O(n)
snprintf (key, sizeof (key)-1, "meta.0x%"PFMT64x, from);
if (exists) {
const char *value = sdb_const_get (DB, key, 0);
int idx = sdb_array_indexof (DB, key, value, 0);
sdb_array_delete (DB, key, idx, 0);
}
snprintf (val, sizeof (val)-1, "%c", type);
sdb_array_add (DB, key, val, 0);
return true;
}
R_API RAnalMetaItem *r_meta_find(RAnal *a, ut64 at, int type, int where) {
const char *infos, *metas;
char key[100];
Sdb *s = a->sdb_meta;
static RAnalMetaItem mi = {0};
// XXX: return allocated item? wtf
if (where != R_META_WHERE_HERE) {
eprintf ("THIS WAS NOT SUPOSED TO HAPPEN\n");
return NULL;
}
snprintf (key, sizeof (key)-1, "meta.0x%"PFMT64x, at);
infos = sdb_const_get (s, key, 0);
if (!infos)
return NULL;
for (; *infos; infos++) {
/* XXX wtf, must use anal.meta.deserialize() */
char *p, *q;
if (*infos==',')
continue;
snprintf (key, sizeof (key) - 1, "meta.%c.0x%"PFMT64x, *infos, at);
metas = sdb_const_get (s, key, 0);
mi.size = sdb_array_get_num (s, key, 0, 0);
mi.type = *infos;
mi.from = at;
mi.to = at + mi.size;
if (type != R_META_TYPE_ANY && type != mi.type) {
continue;
}
if (metas) {
p = strchr (metas, ',');
if (!p) {
continue;
}
mi.space = atoi (p + 1);
q = strchr (p + 1, ',');
if (!q) {
continue;
}
free (mi.str);
mi.str = (char*)sdb_decode (q + 1, 0);
return &mi;
} else mi.str = NULL;
}
return NULL;
}
R_API const char *r_meta_type_to_string(int type) {
// XXX: use type as '%c'
switch (type) {
case R_META_TYPE_HIDE: return "Ch";
case R_META_TYPE_CODE: return "Cc";
case R_META_TYPE_DATA: return "Cd";
case R_META_TYPE_STRING: return "Cs";
case R_META_TYPE_FORMAT: return "Cf";
case R_META_TYPE_MAGIC: return "Cm";
case R_META_TYPE_COMMENT: return "CCu";
}
return "(...)";
}
static void printmetaitem(RAnal *a, RAnalMetaItem *d, int rad) {
char *pstr, *str;
//eprintf ("%d %d\n", d->space, a->meta_spaces.space_idx);
if (a->meta_spaces.space_idx != -1) {
if (a->meta_spaces.space_idx != d->space) {
return;
}
}
str = r_str_escape (d->str);
if (str || d->type == 'd') {
if (d->type=='s' && !*str) {
free (str);
return;
}
if (!str) {
pstr = "";
} else if (d->type != 'C') {
r_name_filter (str, 0);
pstr = str;
} else pstr = d->str;
// r_str_sanitize (str);
switch (rad) {
case 'j':
a->cb_printf ("{\"offset\":%"PFMT64d", \"type\":\"%s\", \"name\":\"%s\"}",
d->from, r_meta_type_to_string (d->type), str);
break;
case 0:
case 1:
case '*':
default:
switch (d->type) {
case 'a': //var and arg comments
case 'v':
case 'e':
//XXX I think they do not belong to here
break;
case 'C':
{
const char *type = r_meta_type_to_string (d->type);
char *s = sdb_encode ((const ut8*)pstr, -1);
if (!s) s = strdup (pstr);
if (rad) {
if (!strcmp (type, "CCu")) {
a->cb_printf ("%s base64:%s @ 0x%08"PFMT64x"\n",
type, s, d->from);
} else {
a->cb_printf ("%s %s @ 0x%08"PFMT64x"\n",
type, pstr, d->from);
}
} else {
if (!strcmp (type, "CCu")) {
char *mys = r_str_escape (pstr);
a->cb_printf ("0x%08"PFMT64x" %s \"%s\"\n",
d->from, type, mys);
free (mys);
} else {
a->cb_printf ("0x%08"PFMT64x" %s \"%s\"\n",
d->from, type, pstr);
}
}
free (s);
}
break;
case 'h': /* hidden */
case 's': /* string */
if (rad) {
a->cb_printf ("%s %d @ 0x%08"PFMT64x" # %s\n",
r_meta_type_to_string (d->type),
(int)d->size, d->from, pstr);
} else {
// TODO: use b64 here
a->cb_printf ("0x%08"PFMT64x" string[%d] \"%s\"\n",
d->from, (int)d->size, pstr);
}
break;
case 'd': /* data */
if (rad) {
a->cb_printf ("%s %d @ 0x%08"PFMT64x"\n",
r_meta_type_to_string (d->type),
(int)d->size, d->from);
} else {
a->cb_printf ("0x%08"PFMT64x" data %s %d\n",
d->from, r_meta_type_to_string (d->type), (int)d->size);
}
break;
case 'm': /* magic */
case 'f': /* formatted */
if (rad) {
a->cb_printf ("%s %d %s @ 0x%08"PFMT64x"\n",
r_meta_type_to_string (d->type),
(int)d->size, pstr, d->from);
} else {
const char *dtype = d->type=='m'?"magic":"format";
a->cb_printf ("0x%08"PFMT64x" %s %d %s\n",
d->from, dtype, (int)d->size, pstr);
}
break;
default:
if (rad) {
a->cb_printf ("%s %d 0x%08"PFMT64x" # %s\n",
r_meta_type_to_string (d->type),
(int)d->size, d->from, pstr);
} else {
// TODO: use b64 here
a->cb_printf ("0x%08"PFMT64x" array[%d] %s %s\n",
d->from, (int)d->size,
r_meta_type_to_string (d->type), pstr);
}
break;
}
break;
}
if (str)
free (str);
}
}
static int meta_print_item(void *user, const char *k, const char *v) {
// const char *v; // size
const char *v2; // space_idx
RAnalMetaUserItem *ui = user;
RAnalMetaItem it;
if (strlen (k)<8)
return 1;
if (memcmp (k+6, ".0x", 3))
return 1;
it.type = k[5];
it.size = sdb_atoi (v);
it.from = sdb_atoi (k+7);
v2 = strchr (v, ',');
if (!v2) goto beach;
it.space = atoi (v2+1);
it.to = it.from + it.size;
it.str = strchr (v2+1, ',');
if (it.str)
it.str = (char *)sdb_decode ((const char*)it.str+1, 0);
else {
it.str = strdup (it.str? it.str: ""); // don't break in free
if (!it.str) goto beach;
}
printmetaitem (ui->anal, &it, ui->rad);
free (it.str);
beach:
return 1;
}
R_API int r_meta_list_cb(RAnal *a, int type, int rad, SdbForeachCallback cb, void *user) {
RAnalMetaUserItem ui = { a, type, rad, cb, user, 0 };
if (rad=='j') a->cb_printf ("[");
if (cb) {
sdb_foreach (DB, cb, &ui);
} else {
sdb_foreach (DB, meta_print_item, &ui);
}
if (rad=='j') a->cb_printf ("]\n");
return ui.count;
}
R_API int r_meta_list(RAnal *a, int type, int rad) {
return r_meta_list_cb (a, type, rad, NULL, NULL);
}
static int meta_enumerate_cb(void *user, const char *k, const char *v) {
const char *v2;
RAnalMetaUserItem *ui = user;
RList *list = ui->user;
//RAnal *a = ui->anal;
RAnalMetaItem *it;
if (strlen (k)<8)
return 1;
if (memcmp (k+6, ".0x", 3))
return 1;
it = R_NEW0 (RAnalMetaItem);
if (!it) return 0;
it->type = k[5];
it->size = sdb_atoi (v);
it->from = sdb_atoi (k+7);
it->to = it->from + it->size;
v2 = strchr (v, ',');
if (!v2) {
free (it);
goto beach;
}
it->space = atoi (v2+1);
it->str = strchr (v2+1, ',');
if (it->str) {
it->str = (char *)sdb_decode ((const char*)it->str+1, 0);
} else {
free(it);
goto beach;
}
//printmetaitem (ui->anal, &it, ui->rad);
r_list_append (list, it);
beach:
return 1;
}
R_API RList *r_meta_enumerate(RAnal *a, int type) {
RList *list = r_list_new ();
r_meta_list_cb (a, type, 0, meta_enumerate_cb, list);
return list;
}
static int deserialize(RAnalMetaItem *it, const char *k, const char *v) {
const char *v2;
if (strlen (k)<8)
return 1;
if (memcmp (k+6, ".0x", 3))
return 1;
it->type = k[5];
it->size = sdb_atoi (v);
it->from = sdb_atoi (k+7);
it->to = it->from + it->size;
v2 = strchr (v, ',');
if (!v2) goto beach;
it->space = atoi (v2+1);
it->str = strchr (v2+1, ',');
//printmetaitem (ui->anal, &it, ui->rad);
beach:
return 1;
}
static void serialize(RAnalMetaItem *it, char *k, char *v) {
sprintf (k, "meta.%c.0x%"PFMT64x, it->type, it->from);
snprintf (v, 4095, "%d,%d,%s", (int)it->size, it->space, it->str);
}
static int meta_unset_cb(void *user, const char *k, const char *v) {
char nk[128], nv[4096];
RAnalMetaUserItem *ui = user;
RAnal *a = ui->anal;
RAnalMetaItem it = {0};
if (!strstr(k, ".0x"))
return 1;
deserialize (&it, k, v);
if (it.space != -1) {
it.space = -1;
serialize (&it, nk, nv);
sdb_set (DB, nk, nv, 0);
}
return 1;
}
R_API void r_meta_space_unset_for(RAnal *a, int type) {
r_meta_list_cb (a, type, 0, meta_unset_cb, NULL);
}
typedef struct {
int count;
int index;
int ctx;
} myMetaUser;
static int meta_count_cb(void *user, const char *k, const char *v) {
RAnalMetaUserItem *ui = user;
myMetaUser *mu = ui->user;
RAnalMetaItem it = {0};
if (!strstr(k, ".0x"))
return 1;
deserialize (&it, k, v);
if (mu) {
if (it.space == mu->ctx) {
mu->count++;
}
}
return 1;
}
R_API int r_meta_space_count_for(RAnal *a, int ctx) {
myMetaUser mu = {0};
mu.ctx = ctx;
int type = a->meta_spaces.space_idx;
r_meta_list_cb (a, type, 0, meta_count_cb, &mu);
return mu.count;
}