mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-23 13:19:54 +00:00
0626013ac7
* Linux debug plugin, page promotion feature proposal. Providing new 'dmL' debugger sub command. This is the only system having THP feature, others are more into direct huge tlb equivalent ... The system ought to be set in madvise mode and disabled on Android, would not do any good in this environment if this is ever supported. Once the syscall succeed, the address range ought to get the little 'hg' VmFlags. * interface changes
360 lines
10 KiB
C
360 lines
10 KiB
C
/* radare - LGPL - Copyright 2009-2017 - pancake */
|
|
|
|
#include <r_debug.h>
|
|
#include <r_list.h>
|
|
|
|
/* Print out the JSON body for memory maps in the passed map region */
|
|
static void print_debug_map_json(RDebug *dbg, RDebugMap *map, bool prefix_comma) {
|
|
dbg->cb_printf ("%s{", prefix_comma ? ",": "");
|
|
if (map->name && *map->name) {
|
|
char *escaped_name = r_str_escape (map->name);
|
|
dbg->cb_printf ("\"name\":\"%s\",", escaped_name);
|
|
free (escaped_name);
|
|
}
|
|
if (map->file && *map->file) {
|
|
char *escaped_path = r_str_escape (map->file);
|
|
dbg->cb_printf ("\"file\":\"%s\",", escaped_path);
|
|
free (escaped_path);
|
|
}
|
|
dbg->cb_printf ("\"addr\":%" PFMT64u ",", map->addr);
|
|
dbg->cb_printf ("\"addr_end\":%" PFMT64u ",", map->addr_end);
|
|
dbg->cb_printf ("\"type\":\"%c\",", map->user?'u':'s');
|
|
dbg->cb_printf ("\"perm\":\"%s\"", r_str_rwx_i (map->perm));
|
|
dbg->cb_printf ("}");
|
|
}
|
|
|
|
/* Write the memory map header describing the line columns */
|
|
static void print_debug_map_line_header(RDebug *dbg, const char *input) {
|
|
// TODO: Write header to console based on which command is being ran
|
|
}
|
|
|
|
/* Write a single memory map line to the console */
|
|
static void print_debug_map_line(RDebug *dbg, RDebugMap *map, ut64 addr, const char *input) {
|
|
char humansz[8];
|
|
if (input[0] == 'q') { // "dmq"
|
|
char *name = (map->name && *map->name)
|
|
? r_str_newf ("%s.%s", map->name, r_str_rwx_i (map->perm))
|
|
: r_str_newf ("%08" PFMT64x ".%s", map->addr, r_str_rwx_i (map->perm));
|
|
r_name_filter (name, 0);
|
|
r_num_units (humansz, sizeof (humansz), map->addr_end - map->addr);
|
|
dbg->cb_printf ("0x%016" PFMT64x " - 0x%016" PFMT64x " %6s %5s %s\n",
|
|
map->addr,
|
|
map->addr_end,
|
|
humansz,
|
|
r_str_rwx_i (map->perm),
|
|
name
|
|
);
|
|
free (name);
|
|
} else {
|
|
const char *fmtstr = dbg->bits & R_SYS_BITS_64
|
|
? "0x%016" PFMT64x " - 0x%016" PFMT64x " %c %s %6s %c %s %s %s%s%s\n"
|
|
: "0x%08" PFMT64x " - 0x%08" PFMT64x " %c %s %6s %c %s %s %s%s%s\n";
|
|
const char *type = map->shared ? "sys": "usr";
|
|
const char *flagname = dbg->corebind.getName
|
|
? dbg->corebind.getName (dbg->corebind.core, map->addr) : NULL;
|
|
if (!flagname) {
|
|
flagname = "";
|
|
} else if (map->name) {
|
|
char *filtered_name = strdup (map->name);
|
|
r_name_filter (filtered_name, 0);
|
|
if (!strncmp (flagname, "map.", 4) && \
|
|
!strcmp (flagname + 4, filtered_name)) {
|
|
flagname = "";
|
|
}
|
|
free (filtered_name);
|
|
}
|
|
r_num_units (humansz, sizeof (humansz), map->size);
|
|
dbg->cb_printf (fmtstr,
|
|
map->addr,
|
|
map->addr_end,
|
|
(addr >= map->addr && addr < map->addr_end) ? '*' : '-',
|
|
type,
|
|
humansz,
|
|
map->user ? 'u' : 's',
|
|
r_str_rwx_i (map->perm),
|
|
map->name ? map->name : "?",
|
|
map->file ? map->file : "?",
|
|
*flagname ? " ; " : "",
|
|
flagname
|
|
);
|
|
}
|
|
}
|
|
|
|
R_API void r_debug_map_list(RDebug *dbg, ut64 addr, const char *input) {
|
|
int i;
|
|
bool notfirst = false;
|
|
RListIter *iter;
|
|
RDebugMap *map;
|
|
if (!dbg) {
|
|
return;
|
|
}
|
|
|
|
switch (input[0]) {
|
|
case 'j': // "dmj" add JSON opening array brace
|
|
dbg->cb_printf ("[");
|
|
break;
|
|
case '*': // "dm*" don't print a header for r2 commands output
|
|
break;
|
|
default:
|
|
// TODO: Find a way to only print headers if output isn't being grepped
|
|
print_debug_map_line_header (dbg, input);
|
|
}
|
|
|
|
for (i = 0; i < 2; i++) { // Iterate over dbg::maps and dbg::maps_user
|
|
RList *maps = (i == 0) ? dbg->maps : dbg->maps_user;
|
|
r_list_foreach (maps, iter, map) {
|
|
switch (input[0]) {
|
|
case 'j': // "dmj"
|
|
print_debug_map_json (dbg, map, notfirst);
|
|
notfirst = true;
|
|
break;
|
|
case '*': // "dm*"
|
|
{
|
|
char *name = (map->name && *map->name)
|
|
? r_str_newf ("%s.%s", map->name, r_str_rwx_i (map->perm))
|
|
: r_str_newf ("%08" PFMT64x ".%s", map->addr, r_str_rwx_i (map->perm));
|
|
r_name_filter (name, 0);
|
|
dbg->cb_printf ("f map.%s 0x%08" PFMT64x " 0x%08" PFMT64x "\n",
|
|
name, map->addr_end - map->addr + 1, map->addr);
|
|
free (name);
|
|
}
|
|
break;
|
|
case 'q': // "dmq"
|
|
if (input[1] == '.') { // "dmq."
|
|
if (addr >= map->addr && addr < map->addr_end) {
|
|
print_debug_map_line (dbg, map, addr, input);
|
|
}
|
|
break;
|
|
}
|
|
print_debug_map_line (dbg, map, addr, input);
|
|
break;
|
|
case '.':
|
|
if (addr >= map->addr && addr < map->addr_end) {
|
|
print_debug_map_line (dbg, map, addr, input);
|
|
}
|
|
break;
|
|
default:
|
|
print_debug_map_line (dbg, map, addr, input);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (input[0] == 'j') { // "dmj" add JSON closing array brace
|
|
dbg->cb_printf ("]\n");
|
|
}
|
|
}
|
|
|
|
static int cmp(const void *a, const void *b) {
|
|
RDebugMap *ma = (RDebugMap*) a;
|
|
RDebugMap *mb = (RDebugMap*) b;
|
|
return ma->addr - mb->addr;
|
|
}
|
|
|
|
/**
|
|
* \brief Find the min and max addresses in an RList of maps.
|
|
* \param maps RList of maps that will be searched through
|
|
* \param min Pointer to a ut64 that the min will be stored in
|
|
* \param max Pointer to a ut64 that the max will be stored in
|
|
* \param skip How many maps to skip at the start of iteration
|
|
* \param width Divisor for the return value
|
|
* \return (max-min)/width
|
|
*
|
|
* Used to determine the min & max addresses of maps and
|
|
* scale the ascii bar to the width of the terminal
|
|
*/
|
|
static int findMinMax(RList *maps, ut64 *min, ut64 *max, int skip, int width) {
|
|
RDebugMap *map;
|
|
RListIter *iter;
|
|
*min = UT64_MAX;
|
|
*max = 0;
|
|
r_list_foreach (maps, iter, map) {
|
|
if (skip > 0) {
|
|
skip--;
|
|
continue;
|
|
}
|
|
if (map->addr < *min) {
|
|
*min = map->addr;
|
|
}
|
|
if (map->addr_end > *max) {
|
|
*max = map->addr_end;
|
|
}
|
|
}
|
|
return (*max - *min) / width;
|
|
}
|
|
|
|
static void print_debug_maps_ascii_art(RDebug *dbg, RList *maps, ut64 addr, int colors) {
|
|
ut64 mul; // The amount of address space a single console column will represent in bar graph
|
|
ut64 min = -1, max = 0;
|
|
int width = r_cons_get_size (NULL) - 90;
|
|
RListIter *iter;
|
|
RDebugMap *map;
|
|
RConsPrintablePalette *pal = &r_cons_singleton ()->context->pal;
|
|
if (width < 1) {
|
|
width = 30;
|
|
}
|
|
r_list_sort (maps, cmp);
|
|
mul = findMinMax (maps, &min, &max, 0, width);
|
|
ut64 last = min;
|
|
if (min != -1 && mul != 0) {
|
|
const char *color_prefix = ""; // Color escape code prefixed to string (address coloring)
|
|
const char *color_suffix = ""; // Color escape code appended to end of string
|
|
const char *fmtstr;
|
|
char humansz[8]; // Holds the human formatted size string [124K]
|
|
int skip = 0; // Number of maps to skip when re-calculating the minmax
|
|
r_list_foreach (maps, iter, map) {
|
|
r_num_units (humansz, sizeof (humansz), map->size); // Convert map size to human readable string
|
|
if (colors) {
|
|
color_suffix = Color_RESET;
|
|
if ((map->perm & 2) && (map->perm & 1)) { // Writable & Executable
|
|
color_prefix = pal->widget_sel;
|
|
} else if (map->perm & 2) { // Writable
|
|
color_prefix = pal->graph_false;
|
|
} else if (map->perm & 1) { // Executable
|
|
color_prefix = pal->graph_true;
|
|
} else {
|
|
color_prefix = "";
|
|
color_suffix = "";
|
|
}
|
|
} else {
|
|
color_prefix = "";
|
|
color_suffix = "";
|
|
}
|
|
if ((map->addr - last) > UT32_MAX) { // TODO: Comment what this is for
|
|
mul = findMinMax (maps, &min, &max, skip, width); // Recalculate minmax
|
|
}
|
|
skip++;
|
|
fmtstr = dbg->bits & R_SYS_BITS_64 // Prefix formatting string (before bar)
|
|
? "map %4.8s %c %s0x%016" PFMT64x "%s |"
|
|
: "map %4.8s %c %s0x%08" PFMT64x "%s |";
|
|
dbg->cb_printf (fmtstr, humansz,
|
|
(addr >= map->addr && \
|
|
addr < map->addr_end) ? '*' : '-',
|
|
color_prefix, map->addr, color_suffix); // * indicates map is within our current sought offset
|
|
int col;
|
|
for (col = 0; col < width; col++) { // Iterate over the available width/columns for bar graph
|
|
ut64 pos = min + (col * mul); // Current address space to check
|
|
ut64 npos = min + ((col + 1) * mul); // Next address space to check
|
|
if (map->addr < npos && map->addr_end > pos) {
|
|
dbg->cb_printf ("#"); // TODO: Comment what a # represents
|
|
} else {
|
|
dbg->cb_printf ("-");
|
|
}
|
|
}
|
|
fmtstr = dbg->bits & R_SYS_BITS_64 ? // Suffix formatting string (after bar)
|
|
"| %s0x%016" PFMT64x "%s %s %s\n" :
|
|
"| %s0x%08" PFMT64x "%s %s %s\n";
|
|
dbg->cb_printf (fmtstr, color_prefix, map->addr_end, color_suffix,
|
|
r_str_rwx_i (map->perm), map->name);
|
|
last = map->addr;
|
|
}
|
|
}
|
|
}
|
|
|
|
R_API void r_debug_map_list_visual(RDebug *dbg, ut64 addr, const char *input, int colors) {
|
|
if (dbg) {
|
|
int i;
|
|
for (i = 0; i < 2; i++) { // Iterate over dbg::maps and dbg::maps_user
|
|
RList *maps = (i == 0) ? dbg->maps : dbg->maps_user;
|
|
if (maps) {
|
|
RListIter *iter;
|
|
RDebugMap *map;
|
|
if (input[1] == '.') { // "dm=." Only show map overlapping current offset
|
|
dbg->cb_printf ("TODO:\n");
|
|
r_list_foreach (maps, iter, map) {
|
|
if (addr >= map->addr && addr < map->addr_end) {
|
|
// print_debug_map_ascii_art (dbg, map);
|
|
}
|
|
}
|
|
} else { // "dm=" Show all maps with a graph
|
|
print_debug_maps_ascii_art (dbg, maps, addr, colors);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
R_API RDebugMap *r_debug_map_new(char *name, ut64 addr, ut64 addr_end, int perm, int user) {
|
|
RDebugMap *map;
|
|
/* range could be 0k on OpenBSD, it's a honeypot */
|
|
if (!name || addr > addr_end) {
|
|
eprintf ("r_debug_map_new: error (\
|
|
%" PFMT64x ">%" PFMT64x ")\n", addr, addr_end);
|
|
return NULL;
|
|
}
|
|
map = R_NEW0 (RDebugMap);
|
|
if (!map) {
|
|
return NULL;
|
|
}
|
|
map->name = strdup (name);
|
|
map->addr = addr;
|
|
map->addr_end = addr_end;
|
|
map->size = addr_end-addr;
|
|
map->perm = perm;
|
|
map->user = user;
|
|
return map;
|
|
}
|
|
|
|
R_API RList *r_debug_modules_list(RDebug *dbg) {
|
|
return (dbg && dbg->h && dbg->h->modules_get)?
|
|
dbg->h->modules_get (dbg): NULL;
|
|
}
|
|
|
|
R_API int r_debug_map_sync(RDebug *dbg) {
|
|
bool ret = false;
|
|
if (dbg && dbg->h && dbg->h->map_get) {
|
|
RList *newmaps = dbg->h->map_get (dbg);
|
|
if (newmaps) {
|
|
r_list_free (dbg->maps);
|
|
dbg->maps = newmaps;
|
|
ret = true;
|
|
}
|
|
}
|
|
return (int)ret;
|
|
}
|
|
|
|
R_API RDebugMap* r_debug_map_alloc(RDebug *dbg, ut64 addr, int size, bool thp) {
|
|
RDebugMap *map = NULL;
|
|
if (dbg && dbg->h && dbg->h->map_alloc) {
|
|
map = dbg->h->map_alloc (dbg, addr, size, thp);
|
|
}
|
|
return map;
|
|
}
|
|
|
|
R_API int r_debug_map_dealloc(RDebug *dbg, RDebugMap *map) {
|
|
bool ret = false;
|
|
ut64 addr = map->addr;
|
|
if (dbg && dbg->h && dbg->h->map_dealloc) {
|
|
if (dbg->h->map_dealloc (dbg, addr, map->size)) {
|
|
ret = true;
|
|
}
|
|
}
|
|
return (int)ret;
|
|
}
|
|
|
|
R_API RDebugMap *r_debug_map_get(RDebug *dbg, ut64 addr) {
|
|
RDebugMap *map, *ret = NULL;
|
|
RListIter *iter;
|
|
r_list_foreach (dbg->maps, iter, map) {
|
|
if (addr >= map->addr && addr <= map->addr_end) {
|
|
ret = map;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
R_API void r_debug_map_free(RDebugMap *map) {
|
|
free (map->name);
|
|
free (map);
|
|
}
|
|
|
|
R_API RList *r_debug_map_list_new() {
|
|
RList *list = r_list_new ();
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
list->free = (RListFree)r_debug_map_free;
|
|
return list;
|
|
}
|