heap: add support for jemalloc v5.3.0 heap parsing (#5744)

This commit is contained in:
bubblepipe 2026-01-18 16:44:57 +08:00 committed by GitHub
parent 50d4ec3901
commit 2ee0d4a42b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 1407 additions and 243 deletions

View file

@ -3200,6 +3200,10 @@ RZ_API int rz_core_config_init(RzCore *core) {
#endif
SETI("dbg.glibc.fastbinmax", 10, "Upper bound on the number of fastbins printed");
n = NODECB("dbg.jemalloc.version", "auto", NULL);
SETDESC(n, "Select jemalloc version for heap parsing (auto-detected if 'auto')");
SETOPTIONS(n, "auto", "4.5.0", "5.3.0", NULL);
SETBPREF("esil.prestep", "true", "Step before esil evaluation in `de` commands");
SETPREF("esil.fillstack", "", "Initialize ESIL stack with (random, debrujn, sequence, zeros, ...)");
SETICB("esil.verbose", 0, &cb_esilverbose, "Show ESIL verbose level (0, 1, 2)");

View file

@ -1048,6 +1048,16 @@ RZ_IPI RzCmdStatus rz_cmd_debug_heap_jemalloc_c_handler(RzCore *core, int argc,
return call_map_jemalloc(core, 'c', argv[1]);
}
// "dmxe" - Find extent for malloc address
RZ_IPI RzCmdStatus rz_cmd_debug_heap_jemalloc_e_handler(RzCore *core, int argc, const char **argv) {
return call_map_jemalloc(core, 'e', argc == 1 ? "" : argv[1]);
}
// "dmxei" - Display extent info
RZ_IPI RzCmdStatus rz_cmd_debug_heap_jemalloc_ei_handler(RzCore *core, int argc, const char **argv) {
return call_map_jemalloc(core, 'i', argv[1]);
}
static void backtrace_vars(RzCore *core, RzList /*<RzDebugFrame *>*/ *frames) {
RzDebugFrame *f;
RzListIter *iter;

View file

@ -937,22 +937,37 @@ commands:
summary: Show all arenas created, or print arena_type structure for given arena.
cname: cmd_debug_heap_jemalloc_a
args:
- name: arena_type
- name: arena_addr
type: RZ_CMD_ARG_TYPE_STRING
optional: true
- name: dmxb
summary: Show all arenas created, or print arena_type structure for given arena.
summary: Show bin info for allocations.
cname: cmd_debug_heap_jemalloc_b
args:
- name: arena_type
- name: arena_addr|bin_info_addr
type: RZ_CMD_ARG_TYPE_STRING
optional: true
- name: dmxc
summary: Show all chunks created in all arenas, or show all chunks created for a given arena_t instanc.
summary: >-
Show all chunks created in all arenas, or show all chunks created for a given
arena_t instance (jemalloc 4.5.0 only).
cname: cmd_debug_heap_jemalloc_c
args:
- name: "*|arena_type"
type: RZ_CMD_ARG_TYPE_STRING
- name: dmxe
summary: List all extents, or find extent for a specific malloc'd address (jemalloc 5.3.0 only)
cname: cmd_debug_heap_jemalloc_e
args:
- name: malloc_addr
type: RZ_CMD_ARG_TYPE_STRING
optional: true
- name: dmxei
summary: Display extent (edata_t) structure info for a given extent address (jemalloc 5.3.0 only)
cname: cmd_debug_heap_jemalloc_ei
args:
- name: extent_addr
type: RZ_CMD_ARG_TYPE_STRING
- name: dp
summary: List or attach to process or thread
subcommands:

View file

@ -519,6 +519,8 @@ static const RzCmdDescArg cmd_debug_process_heap_block_args[2];
static const RzCmdDescArg cmd_debug_heap_jemalloc_a_args[2];
static const RzCmdDescArg cmd_debug_heap_jemalloc_b_args[2];
static const RzCmdDescArg cmd_debug_heap_jemalloc_c_args[2];
static const RzCmdDescArg cmd_debug_heap_jemalloc_e_args[2];
static const RzCmdDescArg cmd_debug_heap_jemalloc_ei_args[2];
static const RzCmdDescArg cmd_debug_pid_list_args[2];
static const RzCmdDescArg cmd_debug_pid_attach_args[2];
static const RzCmdDescArg cmd_debug_pid_detach_args[2];
@ -10829,7 +10831,7 @@ static const RzCmdDescHelp dmx_help = {
};
static const RzCmdDescArg cmd_debug_heap_jemalloc_a_args[] = {
{
.name = "arena_type",
.name = "arena_addr",
.type = RZ_CMD_ARG_TYPE_STRING,
.flags = RZ_CMD_ARG_FLAG_LAST,
.optional = true,
@ -10844,7 +10846,7 @@ static const RzCmdDescHelp cmd_debug_heap_jemalloc_a_help = {
static const RzCmdDescArg cmd_debug_heap_jemalloc_b_args[] = {
{
.name = "arena_type",
.name = "arena_addr|bin_info_addr",
.type = RZ_CMD_ARG_TYPE_STRING,
.flags = RZ_CMD_ARG_FLAG_LAST,
.optional = true,
@ -10853,7 +10855,7 @@ static const RzCmdDescArg cmd_debug_heap_jemalloc_b_args[] = {
{ 0 },
};
static const RzCmdDescHelp cmd_debug_heap_jemalloc_b_help = {
.summary = "Show all arenas created, or print arena_type structure for given arena.",
.summary = "Show bin info for allocations.",
.args = cmd_debug_heap_jemalloc_b_args,
};
@ -10867,10 +10869,39 @@ static const RzCmdDescArg cmd_debug_heap_jemalloc_c_args[] = {
{ 0 },
};
static const RzCmdDescHelp cmd_debug_heap_jemalloc_c_help = {
.summary = "Show all chunks created in all arenas, or show all chunks created for a given arena_t instanc.",
.summary = "Show all chunks created in all arenas, or show all chunks created for a given arena_t instance (jemalloc 4.5.0 only).",
.args = cmd_debug_heap_jemalloc_c_args,
};
static const RzCmdDescArg cmd_debug_heap_jemalloc_e_args[] = {
{
.name = "malloc_addr",
.type = RZ_CMD_ARG_TYPE_STRING,
.flags = RZ_CMD_ARG_FLAG_LAST,
.optional = true,
},
{ 0 },
};
static const RzCmdDescHelp cmd_debug_heap_jemalloc_e_help = {
.summary = "List all extents, or find extent for a specific malloc'd address (jemalloc 5.3.0 only)",
.args = cmd_debug_heap_jemalloc_e_args,
};
static const RzCmdDescArg cmd_debug_heap_jemalloc_ei_args[] = {
{
.name = "extent_addr",
.type = RZ_CMD_ARG_TYPE_STRING,
.flags = RZ_CMD_ARG_FLAG_LAST,
},
{ 0 },
};
static const RzCmdDescHelp cmd_debug_heap_jemalloc_ei_help = {
.summary = "Display extent (edata_t) structure info for a given extent address (jemalloc 5.3.0 only)",
.args = cmd_debug_heap_jemalloc_ei_args,
};
static const RzCmdDescHelp dp_help = {
.summary = "List or attach to process or thread",
};
@ -23401,6 +23432,12 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) {
RzCmdDesc *cmd_debug_heap_jemalloc_c_cd = rz_cmd_desc_argv_new(core->rcmd, dmx_cd, "dmxc", rz_cmd_debug_heap_jemalloc_c_handler, &cmd_debug_heap_jemalloc_c_help);
rz_warn_if_fail(cmd_debug_heap_jemalloc_c_cd);
RzCmdDesc *cmd_debug_heap_jemalloc_e_cd = rz_cmd_desc_argv_new(core->rcmd, dmx_cd, "dmxe", rz_cmd_debug_heap_jemalloc_e_handler, &cmd_debug_heap_jemalloc_e_help);
rz_warn_if_fail(cmd_debug_heap_jemalloc_e_cd);
RzCmdDesc *cmd_debug_heap_jemalloc_ei_cd = rz_cmd_desc_argv_new(core->rcmd, dmx_cd, "dmxei", rz_cmd_debug_heap_jemalloc_ei_handler, &cmd_debug_heap_jemalloc_ei_help);
rz_warn_if_fail(cmd_debug_heap_jemalloc_ei_cd);
RzCmdDesc *dp_cd = rz_cmd_desc_group_state_new(core->rcmd, d_cd, "dp", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_TABLE, rz_cmd_debug_pid_list_handler, &cmd_debug_pid_list_help, &dp_help);
rz_warn_if_fail(dp_cd);
RzCmdDesc *cmd_debug_pid_attachable_list_cd = rz_cmd_desc_argv_state_new(core->rcmd, dp_cd, "dpl", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_TABLE, rz_cmd_debug_pid_attachable_list_handler, &cmd_debug_pid_attachable_list_help);

View file

@ -1371,6 +1371,10 @@ RZ_IPI RzCmdStatus rz_cmd_debug_heap_jemalloc_a_handler(RzCore *core, int argc,
RZ_IPI RzCmdStatus rz_cmd_debug_heap_jemalloc_b_handler(RzCore *core, int argc, const char **argv);
// "dmxc"
RZ_IPI RzCmdStatus rz_cmd_debug_heap_jemalloc_c_handler(RzCore *core, int argc, const char **argv);
// "dmxe"
RZ_IPI RzCmdStatus rz_cmd_debug_heap_jemalloc_e_handler(RzCore *core, int argc, const char **argv);
// "dmxei"
RZ_IPI RzCmdStatus rz_cmd_debug_heap_jemalloc_ei_handler(RzCore *core, int argc, const char **argv);
// "dp"
RZ_IPI RzCmdStatus rz_cmd_debug_pid_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
// "dpl"

View file

@ -4,6 +4,8 @@
#ifndef INCLUDE_HEAP_JEMALLOC_STD_C
#define INCLUDE_HEAP_JEMALLOC_STD_C
#include "rz_util/rz_log.h"
#include "time.h"
#define HEAP32 1
#include "linux_heap_jemalloc.c"
#undef HEAP32
@ -11,22 +13,26 @@
#undef GH
#undef GHT
#undef GHST
#undef GHT_MAX
#undef PFMTx
#undef GH_IS_64
#if HEAP32
#define GH(x) x##_32
#define GHT ut32
#define GHST st32
#define GHT_MAX UT32_MAX
#define PFMTx PFMT32x
#else
#define GH(x) x##_64
#define GHT ut64
#define GHST st64
#define GHT_MAX UT64_MAX
#define PFMTx PFMT64x
#define GH_IS_64
#endif
#if __linux__
static GHT GH(je_get_va_symbol)(RzCore *core, const char *path, const char *sym_name) {
GHT vaddr = GHT_MAX;
RzBin *bin = core->bin;
@ -61,59 +67,97 @@ static GHT GH(je_get_va_symbol)(RzCore *core, const char *path, const char *sym_
return vaddr;
}
#endif
static bool GH(rz_resolve_jemalloc)(RzCore *core, char *symname, ut64 *symbol) {
RzListIter *iter;
RzDebugMap *map;
const char *jemalloc_ver_end = NULL;
const char *jemalloc_path = NULL;
ut64 jemalloc_addr = UT64_MAX;
const char *binary_path = NULL;
ut64 binary_addr = UT64_MAX;
if (!core || !core->dbg || !core->dbg->maps) {
return false;
}
rz_debug_map_sync(core->dbg);
rz_list_foreach (core->dbg->maps, iter, map) {
if (strstr(map->name, "libjemalloc.")) {
jemalloc_addr = map->addr;
jemalloc_ver_end = map->name;
break;
if (jemalloc_addr == UT64_MAX || map->addr < jemalloc_addr) {
jemalloc_addr = map->addr;
jemalloc_path = map->name;
}
}
if (!strstr(map->name, ".so") && !strstr(map->name, "lib") &&
!strstr(map->name, "[") && strlen(map->name) > 0) {
if (binary_addr == UT64_MAX || map->addr < binary_addr) {
binary_addr = map->addr;
binary_path = map->name;
}
}
}
if (!jemalloc_ver_end) {
RZ_LOG_WARN("Is jemalloc mapped in memory? (see dm command)\n");
return false;
}
#if __linux__
char *path = rz_str_newf("%s", jemalloc_ver_end);
if (rz_file_exists(path)) {
ut64 vaddr = GH(je_get_va_symbol)(core, path, symname);
if (jemalloc_addr != GHT_MAX && vaddr != 0) {
*symbol = jemalloc_addr + vaddr;
free(path);
return true;
/* Try dynamic library first */
if (jemalloc_path) {
char *path = rz_str_newf("%s", jemalloc_path);
if (rz_file_exists(path)) {
GHT vaddr = GH(je_get_va_symbol)(core, path, symname);
if (jemalloc_addr != GHT_MAX && vaddr != GHT_MAX) {
*symbol = jemalloc_addr + vaddr;
free(path);
return true;
}
}
free(path);
}
/* Fall back to static linking */
if (binary_path) {
char *path = rz_str_newf("%s", binary_path);
if (rz_file_exists(path)) {
GHT vaddr = GH(je_get_va_symbol)(core, path, symname);
if (binary_addr != GHT_MAX && vaddr != GHT_MAX) {
*symbol = binary_addr + vaddr;
free(path);
return true;
}
}
free(path);
}
free(path);
return false;
#else
(void)jemalloc_addr;
RZ_LOG_INFO("Resolving %s from libjemalloc.2... ", symname);
// this is quite sloooow, we must optimize dmi
char *va = rz_core_cmd_strf(core, "dmi libjemalloc.2 %s$~[1]", symname);
ut64 n = rz_num_get(NULL, va);
if (n && n != UT64_MAX) {
*symbol = n;
rz_cons_printf("0x%08" PFMT64x "\n", n);
} else {
rz_cons_printf("NOT FOUND\n");
}
free(va);
return true;
#endif
}
static void GH(jemalloc_get_chunks)(RzCore *core, const char *input) {
/**
* \brief Detect jemalloc version by checking for version-specific symbols
*
* jemalloc 4.x has je_chunksize symbol (chunk-based architecture)
* jemalloc 5.x does NOT have je_chunksize (extent-based architecture)
*/
static bool GH(rz_jemalloc_detect_version)(RzCore *core) {
ut64 chunksize_addr;
const char *current_version = rz_config_get(core->config, "dbg.jemalloc.version");
// Try to resolve je_chunksize - only exists in jemalloc 4.5.0
if (GH(rz_resolve_jemalloc)(core, "je_chunksize", &chunksize_addr)) {
// je_chunksize found -> jemalloc 4.5.0
if (strcmp(current_version, "4.5.0") != 0) {
rz_config_set(core->config, "dbg.jemalloc.version", "4.5.0");
RZ_LOG_INFO("Detected jemalloc 4.5.0 (je_chunksize symbol found)\n");
}
return true;
} else if (GH(rz_resolve_jemalloc)(core, "je_arena_emap_global", &chunksize_addr)) {
if (strcmp(current_version, "5.3.0") != 0) {
rz_config_set(core->config, "dbg.jemalloc.version", "5.3.0");
RZ_LOG_INFO("Detected jemalloc 5.3.0 (je_arena_emap_global symbol found)\n");
}
return true;
} else {
rz_config_set(core->config, "dbg.jemalloc.version", "NULL");
RZ_LOG_WARN("jemalloc version cannot be determined\n");
return false;
}
}
static void GH(jemalloc_get_chunks_450)(RzCore *core, const char *input) {
ut64 cnksz;
RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal;
@ -124,17 +168,17 @@ static void GH(jemalloc_get_chunks)(RzCore *core, const char *input) {
rz_io_read_at_mapped(core->io, cnksz, (ut8 *)&cnksz, sizeof(GHT));
if (input[0] == '\0') {
RZ_LOG_ERROR("need an arena_t to associate chunks\n");
RZ_LOG_ERROR("need an arena_t_450 to associate chunks\n");
} else if (input[0] != '*') {
const char *addr_str = (input[0] == ' ') ? input + 1 : input;
GHT arena = GHT_MAX;
arena_t *ar = RZ_NEW0(arena_t);
extent_node_t *node = RZ_NEW0(extent_node_t), *head = RZ_NEW0(extent_node_t);
arena_t_450 *ar = RZ_NEW0(arena_t_450);
extent_node_t_450 *node = RZ_NEW0(extent_node_t_450), *head = RZ_NEW0(extent_node_t_450);
arena = rz_num_math(core->num, addr_str);
if (arena) {
rz_io_read_at_mapped(core->io, arena, (ut8 *)ar, sizeof(arena_t));
rz_io_read_at_mapped(core->io, (GHT)(size_t)ar->achunks.qlh_first, (ut8 *)head, sizeof(extent_node_t));
rz_io_read_at_mapped(core->io, arena, (ut8 *)ar, sizeof(arena_t_450));
rz_io_read_at_mapped(core->io, (GHT)(size_t)ar->achunks.qlh_first, (ut8 *)head, sizeof(extent_node_t_450));
if (head->en_addr) {
PRINT_YA(" Chunk - start: ");
PRINTF_BA("0x%08" PFMT64x, (ut64)(size_t)head->en_addr);
@ -142,7 +186,7 @@ static void GH(jemalloc_get_chunks)(RzCore *core, const char *input) {
PRINTF_BA("0x%08" PFMT64x, (ut64)head->en_addr + cnksz);
PRINT_YA(", size: ");
PRINTF_BA("0x%08" PFMT64x "\n", (ut64)cnksz);
rz_io_read_at_mapped(core->io, (ut64)(size_t)head->ql_link.qre_next, (ut8 *)node, sizeof(extent_node_t));
rz_io_read_at_mapped(core->io, (ut64)(size_t)head->ql_link.qre_next, (ut8 *)node, sizeof(extent_node_t_450));
while (node && node->en_addr != head->en_addr) {
PRINT_YA(" Chunk - start: ");
PRINTF_BA("0x%08" PFMT64x, (ut64)(size_t)node->en_addr);
@ -150,7 +194,7 @@ static void GH(jemalloc_get_chunks)(RzCore *core, const char *input) {
PRINTF_BA("0x%" PFMT64x, (ut64)node->en_addr + cnksz);
PRINT_YA(", size: ");
PRINTF_BA("0x%08" PFMT64x "\n", cnksz);
rz_io_read_at_mapped(core->io, (ut64)(size_t)node->ql_link.qre_next, (ut8 *)node, sizeof(extent_node_t));
rz_io_read_at_mapped(core->io, (ut64)(size_t)node->ql_link.qre_next, (ut8 *)node, sizeof(extent_node_t_450));
}
}
}
@ -161,12 +205,12 @@ static void GH(jemalloc_get_chunks)(RzCore *core, const char *input) {
int i = 0;
ut64 sym;
GHT arenas = GHT_MAX, arena = GHT_MAX;
arena_t *ar = RZ_NEW0(arena_t);
extent_node_t *node = RZ_NEW0(extent_node_t);
extent_node_t *head = RZ_NEW0(extent_node_t);
arena_t_450 *ar = RZ_NEW0(arena_t_450);
extent_node_t_450 *node = RZ_NEW0(extent_node_t_450);
extent_node_t_450 *head = RZ_NEW0(extent_node_t_450);
if (!node || !head) {
RZ_LOG_ERROR("Failed to allocate extent_node_t\n");
RZ_LOG_ERROR("Failed to allocate extent_node_t_450\n");
free(ar);
free(node);
free(head);
@ -181,8 +225,8 @@ static void GH(jemalloc_get_chunks)(RzCore *core, const char *input) {
break;
}
PRINTF_GA("arenas[%d]: @ 0x%" PFMTx " { \n", i++, (GHT)arena);
rz_io_read_at_mapped(core->io, arena, (ut8 *)ar, sizeof(arena_t));
rz_io_read_at_mapped(core->io, (GHT)(size_t)ar->achunks.qlh_first, (ut8 *)head, sizeof(extent_node_t));
rz_io_read_at_mapped(core->io, arena, (ut8 *)ar, sizeof(arena_t_450));
rz_io_read_at_mapped(core->io, (GHT)(size_t)ar->achunks.qlh_first, (ut8 *)head, sizeof(extent_node_t_450));
if (head->en_addr != 0) {
PRINT_YA(" Chunk - start: ");
PRINTF_BA("0x%08" PFMT64x, (ut64)(size_t)head->en_addr);
@ -191,7 +235,7 @@ static void GH(jemalloc_get_chunks)(RzCore *core, const char *input) {
PRINT_YA(", size: ");
PRINTF_BA("0x%08" PFMT64x "\n", (ut64)cnksz);
ut64 addr = (ut64)(size_t)head->ql_link.qre_next;
rz_io_read_at_mapped(core->io, addr, (ut8 *)node, sizeof(extent_node_t));
rz_io_read_at_mapped(core->io, addr, (ut8 *)node, sizeof(extent_node_t_450));
while (node && head && node->en_addr != head->en_addr) {
PRINT_YA(" Chunk - start: ");
PRINTF_BA("0x%08" PFMT64x, (ut64)(size_t)node->en_addr);
@ -199,7 +243,7 @@ static void GH(jemalloc_get_chunks)(RzCore *core, const char *input) {
PRINTF_BA("0x%" PFMT64x, (ut64)node->en_addr + cnksz);
PRINT_YA(", size: ");
PRINTF_BA("0x%" PFMT64x "\n", cnksz);
rz_io_read_at_mapped(core->io, (GHT)(size_t)node->ql_link.qre_next, (ut8 *)node, sizeof(extent_node_t));
rz_io_read_at_mapped(core->io, (GHT)(size_t)node->ql_link.qre_next, (ut8 *)node, sizeof(extent_node_t_450));
}
}
PRINT_GA("}\n");
@ -211,15 +255,15 @@ static void GH(jemalloc_get_chunks)(RzCore *core, const char *input) {
}
}
static void GH(jemalloc_print_narenas)(RzCore *core, const char *input) {
static void GH(jemalloc_print_narenas_450)(RzCore *core, const char *input) {
ut64 symaddr;
ut64 arenas;
GHT arena = GHT_MAX;
arena_t *ar = RZ_NEW0(arena_t);
arena_t_450 *ar = RZ_NEW0(arena_t_450);
if (!ar) {
return;
}
arena_stats_t *stats = RZ_NEW0(arena_stats_t);
GH(arena_stats_t_450) *stats = RZ_NEW0(GH(arena_stats_t_450));
if (!stats) {
free(ar);
return;
@ -265,9 +309,9 @@ static void GH(jemalloc_print_narenas)(RzCore *core, const char *input) {
// Handle address argument (with or without leading space)
const char *addr_str = (input[0] == ' ') ? input + 1 : input;
arena = rz_num_math(core->num, addr_str);
rz_io_read_at_mapped(core->io, (GHT)arena, (ut8 *)ar, sizeof(arena_t));
rz_io_read_at_mapped(core->io, (GHT)arena, (ut8 *)ar, sizeof(arena_t_450));
PRINT_GA("struct arena_s {\n");
#define OO(x) (ut64)(arena + rz_offsetof(arena_t, x))
#define OO(x) (ut64)(arena + rz_offsetof(arena_t_450, x))
PRINTF_BA(" ind = 0x%x\n", ar->ind);
PRINTF_BA(" nthreads: application allocation = 0x%" PFMT64x "\n", (ut64)ar->nthreads[0]);
PRINTF_BA(" nthreads: internal metadata allocation = 0x%" PFMT64x "\n", (ut64)ar->nthreads[1]);
@ -308,20 +352,20 @@ static void GH(jemalloc_print_narenas)(RzCore *core, const char *input) {
}
// Helper to print bin info for a single arena
static void GH(jemalloc_print_arena_bins)(RzCore *core, GHT arena, ut64 bin_info, RzConsPrintablePalette *pal) {
static void GH(jemalloc_print_arena_bins_450)(RzCore *core, GHT arena, ut64 bin_info, RzConsPrintablePalette *pal) {
int j;
arena_t *ar = RZ_NEW0(arena_t);
arena_bin_info_t *b = RZ_NEW0(arena_bin_info_t);
arena_t_450 *ar = RZ_NEW0(arena_t_450);
arena_bin_info_t_450 *b = RZ_NEW0(arena_bin_info_t_450);
if (!ar || !b) {
free(ar);
free(b);
return;
}
rz_io_read_at_mapped(core->io, arena, (ut8 *)ar, sizeof(arena_t));
rz_io_read_at_mapped(core->io, arena, (ut8 *)ar, sizeof(arena_t_450));
for (j = 0; j < JM_NBINS; j++) {
rz_io_read_at_mapped(core->io, (GHT)(bin_info + j * sizeof(arena_bin_info_t)),
(ut8 *)b, sizeof(arena_bin_info_t));
rz_io_read_at_mapped(core->io, (GHT)(bin_info + j * sizeof(arena_bin_info_t_450)),
(ut8 *)b, sizeof(arena_bin_info_t_450));
PRINT_YA(" {\n");
PRINT_YA(" regsize : ");
PRINTF_BA("0x%" PFMT64x "\n", (ut64)b->reg_size);
@ -341,7 +385,7 @@ static void GH(jemalloc_print_arena_bins)(RzCore *core, GHT arena, ut64 bin_info
free(b);
}
static void GH(jemalloc_get_bins)(RzCore *core, const char *input) {
static void GH(jemalloc_get_bins_450)(RzCore *core, const char *input) {
int i = 0;
ut64 bin_info;
ut64 arenas;
@ -363,7 +407,7 @@ static void GH(jemalloc_get_bins)(RzCore *core, const char *input) {
break;
}
PRINTF_YA(" arenas[%d]: @ 0x%" PFMTx " {\n", i++, (GHT)arena);
GH(jemalloc_print_arena_bins)(core, arena, bin_info, pal);
GH(jemalloc_print_arena_bins_450)(core, arena, bin_info, pal);
PRINT_YA(" }\n");
}
}
@ -386,112 +430,516 @@ static void GH(jemalloc_get_bins)(RzCore *core, const char *input) {
bin_info = rz_num_math(core->num, bin_info_str);
PRINTF_GA("arena_t @ 0x%" PFMT64x " bins[%d] {\n", (ut64)arena, JM_NBINS);
GH(jemalloc_print_arena_bins)(core, arena, bin_info, pal);
GH(jemalloc_print_arena_bins_450)(core, arena, bin_info, pal);
PRINT_GA("}\n");
free(args);
}
}
#if 0
static void GH(jemalloc_get_runs)(RzCore *core, const char *input) {
switch (input[0]) {
case ' ':
{
int pageind;
ut64 npages, chunksize_mask, map_bias, map_misc_offset, chunk, mapbits;;
arena_chunk_t *c = RZ_NEW0 (arena_chunk_t);
if (!c) {
RZ_LOG_ERROR ("Cannot call calloc\n");
return;
#ifndef JEMALLOC_530_DEFINED_ONCE
#define JEMALLOC_530_DEFINED_ONCE
static ut64 je_get_va_symbol_530(RzCore *core, const char *path, const char *sym_name, bool is_64bit) {
ut64 vaddr = UT64_MAX;
RzBin *bin = core->bin;
RzBinFile *current_bf = rz_bin_cur(bin);
void **iter;
RzBinSymbol *s;
RzBinOptions opt;
rz_bin_options_init(&opt, -1, 0, 0, false);
opt.obj_opts.elf_load_sections = rz_config_get_b(core->config, "elf.load.sections");
opt.obj_opts.elf_checks_sections = rz_config_get_b(core->config, "elf.checks.sections");
opt.obj_opts.elf_checks_segments = rz_config_get_b(core->config, "elf.checks.segments");
RzBinFile *libc_bf = rz_bin_open(bin, path, &opt);
if (!libc_bf) {
return vaddr;
}
RzBinObject *o = rz_bin_cur_object(bin);
RzPVector *syms = o ? (RzPVector *)rz_bin_object_get_symbols(o) : NULL;
rz_pvector_foreach (syms, iter) {
s = *iter;
if (!strcmp(s->name, sym_name)) {
vaddr = s->vaddr;
break;
}
}
rz_bin_file_delete(bin, libc_bf);
rz_bin_file_set_obj(current_bf, current_bf->o);
rz_bin_set_cur_binfile(bin, current_bf);
return vaddr;
}
static bool rz_resolve_jemalloc_530(RzCore *core, const char *symname, ut64 *symbol, bool is_64bit) {
RzListIter *iter;
RzDebugMap *map;
const char *jemalloc_path = NULL;
ut64 jemalloc_addr = UT64_MAX;
const char *binary_path = NULL;
ut64 binary_addr = UT64_MAX;
if (!core || !core->dbg || !core->dbg->maps) {
return false;
}
rz_debug_map_sync(core->dbg);
rz_list_foreach (core->dbg->maps, iter, map) {
if (strstr(map->name, "libjemalloc.")) {
if (jemalloc_addr == UT64_MAX || map->addr < jemalloc_addr) {
jemalloc_addr = map->addr;
jemalloc_path = map->name;
}
}
if (!strstr(map->name, ".so") && !strstr(map->name, "lib") &&
!strstr(map->name, "[") && strlen(map->name) > 0) {
if (binary_addr == UT64_MAX || map->addr < binary_addr) {
binary_addr = map->addr;
binary_path = map->name;
}
}
}
/* Try dynamic library first */
if (jemalloc_path) {
char *path = rz_str_newf("%s", jemalloc_path);
if (rz_file_exists(path)) {
ut64 vaddr = je_get_va_symbol_530(core, path, symname, is_64bit);
if (jemalloc_addr != UT64_MAX && vaddr != UT64_MAX) {
*symbol = jemalloc_addr + vaddr;
free(path);
return true;
}
}
free(path);
}
/* Fall back to static linking */
if (binary_path) {
char *path = rz_str_newf("%s", binary_path);
if (rz_file_exists(path)) {
ut64 vaddr = je_get_va_symbol_530(core, path, symname, is_64bit);
if (binary_addr != UT64_MAX && vaddr != UT64_MAX) {
*symbol = binary_addr + vaddr;
free(path);
return true;
}
}
free(path);
}
return false;
}
static void jemalloc_print_extent_info_530(RzCore *core, ut64 edata_addr, bool is_64bit) {
RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal;
edata_t_530 edata;
static const char *state_names[] = { "Active", "Dirty", "Muzzy", "Retained" };
if (!read_and_parse_edata_t_530(core->io, edata_addr, &edata, is_64bit)) {
RZ_LOG_ERROR("Failed to read edata at 0x%" PFMT64x "\n", edata_addr);
return;
}
ut64 e_bits = edata.e_bits;
ut64 e_addr = edata.e_addr;
ut64 e_size = edata.e_size_esn & ~((ut64)(1 << LG_PAGE_530) - 1);
ut32 state = (e_bits & EDATA_BITS_STATE_MASK) >> EDATA_BITS_STATE_SHIFT;
bool slab = (e_bits & EDATA_BITS_SLAB_MASK) >> EDATA_BITS_SLAB_SHIFT;
PRINT_YA("Extent @ ");
PRINTF_BA("0x%" PFMT64x "\n", edata_addr);
PRINT_YA(" Allocated Address: ");
PRINTF_BA("0x%" PFMT64x "\n", e_addr);
PRINT_YA(" Size: ");
PRINTF_BA("0x%" PFMT64x "\n", e_size);
PRINT_YA(" Small class (slab): ");
PRINTF_BA("%s\n", slab ? "true" : "false");
PRINT_YA(" State: ");
PRINTF_BA("%s\n", state < 4 ? state_names[state] : "Unknown");
}
static void jemalloc_enumerate_extents_530(RzCore *core, ut64 rtree_addr, bool is_64bit) {
RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal;
HtUU *seen_extents = ht_uu_new();
if (!seen_extents) {
RZ_LOG_ERROR("Failed to allocate hash table\n");
return;
}
ut32 lg_page, rtree_nsb, bits_per_level, max_subkeys;
ut64 root_offset;
rtree_params_530(is_64bit, &lg_page, &rtree_nsb, &bits_per_level, &max_subkeys, &root_offset);
ut64 root_addr = rtree_addr + root_offset;
size_t node_elm_size = rtree_node_elm_size_530(is_64bit);
size_t leaf_elm_size = rtree_leaf_elm_size_530(is_64bit);
ut32 extent_count = 0;
PRINT_GA("Enumerating extents from rtree...\n");
// Level 1: iterate through root nodes
for (ut32 i = 0; i < max_subkeys; i++) {
rtree_node_elm_t_530 node;
ut64 node_addr = root_addr + i * node_elm_size;
if (!read_and_parse_rtree_node_elm_t_530(core->io, node_addr, &node, is_64bit)) {
continue;
}
ut64 leaf_base = node.child;
if (leaf_base == 0) {
continue;
}
// Level 2: iterate through leaf nodes
for (ut32 j = 0; j < max_subkeys; j++) {
rtree_leaf_elm_t_530 leaf;
ut64 leaf_addr = leaf_base + j * leaf_elm_size;
if (!read_and_parse_rtree_leaf_elm_t_530(core->io, leaf_addr, &leaf, is_64bit)) {
continue;
}
input += 1;
chunk = rz_num_math (core->num, input);
if (!GH(rz_resolve_jemalloc)(core, "je_chunk_npages", &npages)) {
RZ_LOG_ERROR ("Cannot resolve je_chunk_npages\n");
return;
}
if (!GH(rz_resolve_jemalloc)(core, "je_chunksize_mask", &chunksize_mask)) {
RZ_LOG_ERROR ("Cannot resolve je_chunksize_mask\n");
return;
}
if (!GH(rz_resolve_jemalloc)(core, "je_map_bias", &map_bias)) {
RZ_LOG_ERROR ("Cannot resolve je_map_bias\n");
return;
}
if (!GH(rz_resolve_jemalloc)(core, "je_map_misc_offset", &map_misc_offset)) {
RZ_LOG_ERROR ("Cannot resolve je_map_misc_offset\n");
return;
}
rz_io_read_at_mapped (core->io, npages, (ut8*)&npages, sizeof (GHT));
rz_io_read_at_mapped (core->io, chunksize_mask, (ut8*)&chunksize_mask, sizeof (GHT));
rz_io_read_at_mapped (core->io, map_bias, (ut8*)&map_bias, sizeof (GHT));
rz_io_read_at_mapped (core->io, map_misc_offset, (ut8*)&map_misc_offset, sizeof (GHT));
rz_cons_printf ("map_misc_offset 0x%08"PFMT64x"\n", (ut64)map_misc_offset);
rz_io_read_at_mapped (core->io, chunk, (ut8 *)c, sizeof (arena_chunk_t));
mapbits = *(GHT *)&c->map_bits;
rz_cons_printf ("map_bits: 0x%08"PFMT64x"\n", (ut64)mapbits);
uint32_t offset = rz_offsetof (arena_chunk_t, map_bits);
arena_chunk_map_bits_t *dwords = (void *)calloc (sizeof (arena_chunk_map_bits_t), npages);
rz_io_read_at_mapped (core->io, chunk + offset, (ut8*)dwords, sizeof (arena_chunk_map_bits_t) * npages);
rz_cons_printf ("map_bits @ 0x%08"PFMT64x"\n", (ut64)(chunk + offset));
arena_run_t *r = RZ_NEW0 (arena_run_t);
if (!r) {
RZ_LOG_ERROR ("Cannot call calloc\n");
return;
}
for (pageind = map_bias; pageind < npages; pageind++) {
arena_chunk_map_bits_t mapelm = dwords[pageind-map_bias];
if (mapelm.bits & CHUNK_MAP_ALLOCATED) {
// ut64 elm = ((arena_chunk_map_misc_t *)((uintptr_t)chunk + (uintptr_t)map_misc_offset) + pageind-map_bias);
ut64 elm = chunk + map_misc_offset + pageind-map_bias;
rz_cons_printf ("\nelm: 0x%"PFMT64x"\n", elm);
arena_chunk_map_misc_t *m = RZ_NEW0 (arena_chunk_map_misc_t);
if (m) {
ut64 run = elm + rz_offsetof (arena_chunk_map_misc_t, run);
rz_io_read_at_mapped (core->io, elm, (ut8*)m, sizeof (arena_chunk_map_misc_t));
rz_cons_printf ("Small run @ 0x%08"PFMT64x"\n", (ut64)elm);
rz_io_read_at_mapped (core->io, run, (ut8*)r, sizeof (arena_run_t));
rz_cons_printf ("binind: 0x%08"PFMT64x"\n", (ut64)r->binind);
rz_cons_printf ("nfree: 0x%08"PFMT64x"\n", (ut64)r->nfree);
rz_cons_printf ("bitmap: 0x%08"PFMT64x"\n\n", (ut64)*(GHT*)r->bitmap);
free (m);
}
} else if (mapelm.bits & CHUNK_MAP_LARGE) {
ut64 run = (ut64) (size_t) chunk + (pageind << LG_PAGE);
rz_cons_printf ("Large run @ 0x%08"PFMT64x"\n", run);
rz_io_read_at_mapped (core->io, run, (ut8*)r, sizeof (arena_run_t));
rz_cons_printf ("binind: 0x%08"PFMT64x"\n", (ut64)r->binind);
rz_cons_printf ("nfree: 0x%08"PFMT64x"\n", (ut64)r->nfree);
rz_cons_printf ("bitmap: 0x%08"PFMT64x"\n\n", (ut64)*(GHT*)r->bitmap);
ut64 edata_addr;
if (is_64bit) {
// 64-bit uses compact leaf format with le_bits
if (leaf.le_bits == 0) {
continue;
}
edata_addr = rtree_leaf_elm_bits_edata_get_530(leaf.le_bits);
} else {
// 32-bit uses non-compact format with direct le_edata pointer
edata_addr = leaf.le_edata;
}
free (c);
free (r);
}
break;
if (edata_addr == 0) {
continue;
}
// Skip duplicates
bool found = false;
ht_uu_find(seen_extents, edata_addr, &found);
if (found) {
continue;
}
ht_uu_insert(seen_extents, edata_addr, 1);
jemalloc_print_extent_info_530(core, edata_addr, is_64bit);
rz_cons_printf("\n");
extent_count++;
}
}
PRINTF_GA("Total extents found: %u\n", extent_count);
ht_uu_free(seen_extents);
}
static ut64 jemalloc_rtree_lookup_530(RzCore *core, ut64 rtree_addr, ut64 addr, bool is_64bit) {
ut32 lg_page, rtree_nsb, bits_per_level, max_subkeys;
ut64 root_offset;
rtree_params_530(is_64bit, &lg_page, &rtree_nsb, &bits_per_level, &max_subkeys, &root_offset);
// Remove page offset to get the key
ut64 key = addr >> lg_page;
// Calculate indices for each level
ut32 mask = (1U << bits_per_level) - 1;
ut32 root_idx = (key >> bits_per_level) & mask;
ut32 leaf_idx = key & mask;
ut64 root_addr = rtree_addr + root_offset;
size_t node_elm_size = rtree_node_elm_size_530(is_64bit);
size_t leaf_elm_size = rtree_leaf_elm_size_530(is_64bit);
// Read root node to get leaf array base
rtree_node_elm_t_530 node;
ut64 node_addr = root_addr + (ut64)root_idx * node_elm_size;
if (!read_and_parse_rtree_node_elm_t_530(core->io, node_addr, &node, is_64bit)) {
return 0;
}
ut64 leaf_base = node.child;
if (leaf_base == 0) {
return 0;
}
rtree_leaf_elm_t_530 leaf;
ut64 leaf_addr = leaf_base + (ut64)leaf_idx * leaf_elm_size;
if (!read_and_parse_rtree_leaf_elm_t_530(core->io, leaf_addr, &leaf, is_64bit)) {
return 0;
}
if (is_64bit) {
if (leaf.le_bits == 0) {
return 0;
}
return rtree_leaf_elm_bits_edata_get_530(leaf.le_bits);
} else {
return leaf.le_edata;
}
}
#endif
static void jemalloc_find_extent_530(RzCore *core, const char *input, bool is_64bit) {
ut64 je_arena_emap_global_addr;
RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal;
if (input[0] == '\0') {
/* No argument: enumerate all extents */
if (rz_resolve_jemalloc_530(core, "je_arena_emap_global", &je_arena_emap_global_addr, is_64bit)) {
jemalloc_enumerate_extents_530(core, je_arena_emap_global_addr, is_64bit);
} else {
RZ_LOG_ERROR("Cannot resolve je_arena_emap_global\n");
}
} else {
/* Address argument: lookup single address in rtree */
const char *addr_str = (input[0] == ' ') ? input + 1 : input;
ut64 lookup_addr = rz_num_math(core->num, addr_str);
if (!rz_resolve_jemalloc_530(core, "je_arena_emap_global", &je_arena_emap_global_addr, is_64bit)) {
RZ_LOG_ERROR("Cannot resolve je_arena_emap_global\n");
return;
}
ut64 edata_addr = jemalloc_rtree_lookup_530(core, je_arena_emap_global_addr, lookup_addr, is_64bit);
if (edata_addr == 0) {
PRINTF_RA("No extent found for address 0x%" PFMT64x "\n", lookup_addr);
return;
}
jemalloc_print_extent_info_530(core, edata_addr, is_64bit);
}
}
static void jemalloc_extent_info_530(RzCore *core, const char *input, bool is_64bit) {
if (input[0] == '\0') {
RZ_LOG_ERROR("Usage: dmxei <edata_addr>\n");
return;
}
const char *addr_str = (input[0] == ' ') ? input + 1 : input;
ut64 edata_addr = rz_num_math(core->num, addr_str);
jemalloc_print_extent_info_530(core, edata_addr, is_64bit);
}
static void jemalloc_print_arena_bins_530(RzCore *core, ut64 arena, ut64 bin_info_addr, bool is_64bit) {
RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal;
bin_info_t_530 bin_info;
bin_t_530 bin;
ut64 bins_off = bins_offset_530(is_64bit);
size_t bin_info_size = bin_info_size_530(is_64bit);
size_t bin_size = bin_size_530(is_64bit);
for (int j = 0; j < JM_NBINS_530; j++) {
if (!read_and_parse_bin_info_t_530(core->io, bin_info_addr + j * bin_info_size, &bin_info, is_64bit)) {
continue;
}
ut64 bin_addr = arena + bins_off + j * bin_size;
if (!read_and_parse_bin_t_530(core->io, bin_addr, &bin, is_64bit)) {
continue;
}
PRINTF_YA(" bin[%d] @ 0x%" PFMT64x " {\n", j, bin_addr);
PRINT_YA(" reg_size : ");
PRINTF_BA("0x%" PFMT64x "\n", bin_info.reg_size);
PRINT_YA(" slab_size : ");
PRINTF_BA("0x%" PFMT64x "\n", bin_info.slab_size);
PRINT_YA(" nregs : ");
PRINTF_BA("0x%x\n", bin_info.nregs);
PRINT_YA(" n_shards : ");
PRINTF_BA("0x%x\n", bin_info.n_shards);
PRINT_YA(" slabcur : ");
PRINTF_BA("0x%" PFMT64x "\n", bin.slabcur);
PRINT_YA(" }\n");
}
}
static void jemalloc_get_bins_530(RzCore *core, const char *input, bool is_64bit) {
int i = 0;
ut64 bin_info;
ut64 arenas_sym;
ut64 arena = UT64_MAX;
RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal;
size_t ptr_size = is_64bit ? 8 : 4;
if (input[0] == '\0') {
// No argument - use symbol resolution (debug mode)
if (!rz_resolve_jemalloc_530(core, "je_bin_infos", &bin_info, is_64bit)) {
RZ_LOG_ERROR("Cannot resolve je_bin_infos\n");
return;
}
if (rz_resolve_jemalloc_530(core, "je_arenas", &arenas_sym, is_64bit)) {
PRINTF_GA("arenas @ 0x%" PFMT64x " {\n", arenas_sym);
for (;;) {
if (is_64bit) {
rz_io_read_at_mapped(core->io, arenas_sym + i * ptr_size, (ut8 *)&arena, ptr_size);
} else {
ut32 arena32 = 0;
rz_io_read_at_mapped(core->io, arenas_sym + i * ptr_size, (ut8 *)&arena32, ptr_size);
arena = arena32;
}
if (!arena) {
break;
}
PRINTF_YA(" arenas[%d]: @ 0x%" PFMT64x " {\n", i++, arena);
jemalloc_print_arena_bins_530(core, arena, bin_info, is_64bit);
PRINT_YA(" }\n");
}
}
PRINT_GA("}\n");
} else {
// Static mode - requires two arguments: dmxb <arena_addr> <bin_info_addr>
const char *addr_str = (input[0] == ' ') ? input + 1 : input;
char *args = strdup(addr_str);
if (!args) {
return;
}
char *bin_info_str = strchr(args, ' ');
if (!bin_info_str) {
RZ_LOG_ERROR("Usage: dmxb <arena_addr> <bin_info_addr>\n");
free(args);
return;
}
*bin_info_str++ = '\0';
arena = rz_num_math(core->num, args);
bin_info = rz_num_math(core->num, bin_info_str);
PRINTF_GA("arena_t @ 0x%" PFMT64x " bins[%d] {\n", arena, JM_NBINS_530);
jemalloc_print_arena_bins_530(core, arena, bin_info, is_64bit);
PRINT_GA("}\n");
free(args);
}
}
static void jemalloc_print_narenas_530(RzCore *core, const char *input, bool is_64bit) {
ut64 symaddr;
ut64 arenas;
ut64 arena = UT64_MAX;
int i = 0;
ut64 narenas = 0;
RzConsPrintablePalette *pal = &rz_cons_singleton()->context->pal;
size_t ptr_size = is_64bit ? 8 : 4;
if (input[0] == '\0') { // no args, list all arenas
if (rz_resolve_jemalloc_530(core, "narenas_total", &symaddr, is_64bit)) {
if (is_64bit) {
rz_io_read_at_mapped(core->io, symaddr, (ut8 *)&narenas, ptr_size);
} else {
ut32 narenas32 = 0;
rz_io_read_at_mapped(core->io, symaddr, (ut8 *)&narenas32, ptr_size);
narenas = narenas32;
}
PRINTF_GA("narenas : %" PFMT64d "\n", narenas);
}
if (narenas == 0) {
RZ_LOG_ERROR("No arenas allocated.\n");
return;
}
if (narenas == UT64_MAX) {
RZ_LOG_ERROR("Cannot find narenas_total\n");
return;
}
if (rz_resolve_jemalloc_530(core, "je_arenas", &arenas, is_64bit)) {
PRINTF_GA("arenas[%" PFMT64d "] @ 0x%" PFMT64x " {\n", narenas, arenas);
for (i = 0; i < (int)narenas; i++) {
ut64 at = arenas + (i * ptr_size);
if (is_64bit) {
rz_io_read_at_mapped(core->io, at, (ut8 *)&arena, ptr_size);
} else {
ut32 arena32 = 0;
rz_io_read_at_mapped(core->io, at, (ut8 *)&arena32, ptr_size);
arena = arena32;
}
if (!arena) {
PRINTF_YA(" arenas[%d]: (empty)\n", i);
continue;
}
PRINTF_YA(" arenas[%d]: ", i);
PRINTF_BA("@ 0x%" PFMT64x "\n", arena);
}
}
PRINT_GA("}\n");
} else {
const char *addr_str = (input[0] == ' ') ? input + 1 : input;
arena = rz_num_math(core->num, addr_str);
arena_t_530 ar;
if (!read_and_parse_arena_t_530(core->io, arena, &ar, is_64bit)) {
RZ_LOG_ERROR("Failed to read arena at 0x%" PFMT64x "\n", arena);
return;
}
PRINT_GA("struct arena_s {\n");
PRINTF_BA(" ind = 0x%x\n", ar.ind);
PRINTF_BA(" nthreads: application allocation = 0x%x\n", ar.nthreads[0]);
PRINTF_BA(" nthreads: internal metadata allocation = 0x%x\n", ar.nthreads[1]);
PRINTF_BA(" binshard_next = 0x%x\n", ar.binshard_next);
PRINTF_BA(" last_thd = 0x%" PFMT64x "\n", ar.last_thd);
PRINTF_BA(" stats = 0x%" PFMT64x "\n", ar.stats_addr);
PRINTF_BA(" tcache_ql = 0x%" PFMT64x "\n", ar.tcache_ql_addr);
PRINTF_BA(" cache_bin_array_descriptor_ql = 0x%" PFMT64x "\n", ar.cache_bin_array_descriptor_ql_addr);
PRINTF_BA(" tcache_ql_mtx = 0x%" PFMT64x "\n", ar.tcache_ql_mtx_addr);
PRINTF_BA(" dss_prec = 0x%x\n", ar.dss_prec);
PRINTF_BA(" large = 0x%" PFMT64x "\n", ar.large_addr);
PRINTF_BA(" large_mtx = 0x%" PFMT64x "\n", ar.large_mtx_addr);
PRINTF_BA(" pa_shard = 0x%" PFMT64x "\n", ar.pa_shard_addr);
PRINTF_BA(" base = 0x%" PFMT64x "\n", ar.base);
PRINTF_BA(" create_time.ns = 0x%" PFMT64x "\n", ar.create_time_ns);
PRINTF_BA(" bins = 0x%" PFMT64x "\n", ar.bins_addr);
PRINT_GA("}\n");
}
}
static void cmd_dbg_map_jemalloc_530(RzCore *core, char dmx_variant, const char *arg) {
bool is_64bit = core->rasm->bits == 64;
switch (dmx_variant) {
case 'a': // dmxa - print arena
jemalloc_print_narenas_530(core, arg, is_64bit);
break;
case 'b': // dmxb - bin info
jemalloc_get_bins_530(core, arg, is_64bit);
break;
case 'e': // dmxe - find extent for malloc'd address
jemalloc_find_extent_530(core, arg, is_64bit);
break;
case 'i': // dmxei - extent info
jemalloc_extent_info_530(core, arg, is_64bit);
break;
}
}
#endif /* JEMALLOC_530_DEFINED_ONCE */
static void GH(cmd_dbg_map_jemalloc)(RzCore *core, char dmx_variant, const char *arg) {
switch (dmx_variant) {
case 'a': // dmxa
GH(jemalloc_print_narenas)(core, arg);
break;
case 'b': // dmxb
GH(jemalloc_get_bins)(core, arg);
break;
case 'c': // dmxc
GH(jemalloc_get_chunks)(core, arg);
break;
const char *version = rz_config_get(core->config, "dbg.jemalloc.version");
if (!version || strcmp(version, "auto") == 0 || strcmp(version, "NULL") == 0 || version[0] == '\0') {
GH(rz_jemalloc_detect_version)(core);
version = rz_config_get(core->config, "dbg.jemalloc.version");
}
if (version && strcmp(version, "4.5.0") == 0) {
switch (dmx_variant) {
case 'a': // dmxa
GH(jemalloc_print_narenas_450)(core, arg);
break;
case 'b': // dmxb
GH(jemalloc_get_bins_450)(core, arg);
break;
case 'c': // dmxc
GH(jemalloc_get_chunks_450)(core, arg);
break;
}
} else if (version && strcmp(version, "5.3.0") == 0) {
cmd_dbg_map_jemalloc_530(core, dmx_variant, arg);
} else {
RZ_LOG_ERROR("Unknown jemalloc version. Please set dbg.jemalloc.version to '4.5.0' or '5.3.0'\n");
}
}

View file

@ -27,7 +27,8 @@ typedef st32 RZ_ALIGNED(4) GHST_32;
#define GHST GHST_32
#endif
#include <rz_jemalloc/jemalloc.h>
/* jemalloc 4.5.0 still uses the GH/GHT macro system */
#include <rz_jemalloc/jemalloc_450.h>
#ifndef INC_HEAP32
#undef JEMALLOC_INTERNAL_H
@ -42,6 +43,9 @@ typedef st32 RZ_ALIGNED(4) GHST_32;
#ifndef RZ_HEAP_JEMALLOC_H
#define RZ_HEAP_JEMALLOC_H
/* jemalloc 5.3.0 uses unified structs with is_64bit parameter - included once */
#include <rz_jemalloc/jemalloc_530.h>
#undef PRINTF_A
#undef PRINTF_YA
#undef PRINTF_GA

View file

@ -30,12 +30,18 @@
*/
#undef GH_ALIGN
#ifdef GH_IS_64
#define GH_ALIGN __attribute__((aligned(8)))
#define GH_ALIGN RZ_ALIGNED(8)
#else
#define GH_ALIGN __attribute__((aligned(4)))
#define GH_ALIGN RZ_ALIGNED(4)
#endif
/* Queue/list helper macros - pointers become GHT */
#undef RZ_JM_QL_HEAD
#undef RZ_JM_QR
#undef RZ_JM_RB_TREE
#undef RZ_JM_PH
#undef RZ_JM_QL_ELM
#undef RZ_JM_RB_NODE
#define RZ_JM_QL_HEAD(a_type) struct GH_ALIGN { GHT qlh_first; }
#define RZ_JM_QR(a_type) struct GH_ALIGN { GHT qre_next; GHT qre_prev; }
#define RZ_JM_RB_TREE(a_type) struct GH_ALIGN { GHT rbt_root; }
@ -44,8 +50,8 @@
#define RZ_JM_RB_NODE(a_type) struct GH_ALIGN { GHT rbn_left; GHT rbn_right; }
/* only define once */
#ifndef RZ_JM_DEFINE_ONLY_ONCE
#define RZ_JM_DEFINE_ONLY_ONCE
#ifndef RZ_JM_DEFINE_ONLY_ONCE_450
#define RZ_JM_DEFINE_ONLY_ONCE_450
typedef enum {
dss_prec_disabled = 0,
dss_prec_primary = 1,
@ -59,35 +65,33 @@ typedef enum {
* Note: We use __attribute__((aligned(N))) because ut64 only has 4-byte
* alignment on 32-bit hosts, so we can't rely on natural alignment.
*/
typedef struct __attribute__((aligned(4))) { ut8 data[44]; } malloc_mutex_t_32;
typedef struct __attribute__((aligned(8))) { ut8 data[80]; } malloc_mutex_t_64;
typedef struct __attribute__((aligned(4))) { ut8 data[8]; } nstime_t_32;
typedef struct __attribute__((aligned(8))) { ut8 data[8]; } nstime_t_64;
typedef struct __attribute__((aligned(4))) { ut8 data[112]; } prof_tctx_t_32;
typedef struct __attribute__((aligned(8))) { ut8 data[128]; } prof_tctx_t_64;
typedef struct __attribute__((aligned(4))) { ut8 data[96]; } arena_stats_t_32;
typedef struct __attribute__((aligned(8))) { ut8 data[128]; } arena_stats_t_64;
typedef struct __attribute__((aligned(4))) { ut8 data[116]; } arena_bin_t_32;
typedef struct __attribute__((aligned(8))) { ut8 data[168]; } arena_bin_t_64;
typedef struct RZ_ALIGNED(4) { ut8 data[44]; } malloc_mutex_t_450_32;
typedef struct RZ_ALIGNED(8) { ut8 data[80]; } malloc_mutex_t_450_64;
typedef struct RZ_ALIGNED(4) { ut8 data[8]; } nstime_t_450_32;
typedef struct RZ_ALIGNED(8) { ut8 data[8]; } nstime_t_450_64;
typedef struct RZ_ALIGNED(4) { ut8 data[112]; } prof_tctx_t_450_32;
typedef struct RZ_ALIGNED(8) { ut8 data[128]; } prof_tctx_t_450_64;
typedef struct RZ_ALIGNED(4) { ut8 data[96]; } arena_stats_t_450_32;
typedef struct RZ_ALIGNED(8) { ut8 data[128]; } arena_stats_t_450_64;
typedef struct RZ_ALIGNED(4) { ut8 data[116]; } arena_bin_t_450_32;
typedef struct RZ_ALIGNED(8) { ut8 data[168]; } arena_bin_t_450_64;
#endif
typedef struct GH(arena_runs_dirty_link_s) GH(arena_runs_dirty_link_t);
typedef struct GH(arena_bin_info_s) GH(arena_bin_info_t);
typedef struct GH(arena_decay_s) GH(arena_decay_t);
typedef struct GH(arena_s) GH(arena_t);
typedef struct GH(extent_node_s) GH(extent_node_t);
typedef RZ_JM_RB_TREE(extent_node_t) GH(extent_tree_t);
typedef struct GH(bitmap_info_s) GH(bitmap_info_t);
typedef struct GH(bitmap_level_s) GH(bitmap_level_t);
typedef struct GH(arena_runs_dirty_link_s_450) GH(arena_runs_dirty_link_t_450);
typedef struct GH(arena_bin_info_s_450) GH(arena_bin_info_t_450);
typedef struct GH(arena_decay_s_450) GH(arena_decay_t_450);
typedef struct GH(arena_s_450) GH(arena_t_450);
typedef struct GH(extent_node_s_450) GH(extent_node_t_450);
typedef RZ_JM_RB_TREE(extent_node_t_450) GH(extent_tree_t_450);
typedef struct GH(bitmap_info_s_450) GH(bitmap_info_t_450);
typedef struct GH(bitmap_level_s_450) GH(bitmap_level_t_450);
#undef arena_t
#undef extent_node_t
#undef arena_stats_t
#undef arena_bin_info_t
#define arena_t GH(arena_t)
#define extent_node_t GH(extent_node_t)
#define arena_stats_t GH(arena_stats_t)
#define arena_bin_info_t GH(arena_bin_info_t)
#undef arena_t_450
#undef extent_node_t_450
#undef arena_bin_info_t_450
#define arena_t_450 GH(arena_t_450)
#define extent_node_t_450 GH(extent_node_t_450)
#define arena_bin_info_t_450 GH(arena_bin_info_t_450)
/* chunk_hooks_t
* source: https://github.com/jemalloc/jemalloc/blob/4.5.0/include/jemalloc/jemalloc_typedefs.h.in
@ -100,20 +104,20 @@ typedef struct GH_ALIGN {
GHT purge;
GHT split;
GHT merge;
} GH(chunk_hooks_t);
} GH(chunk_hooks_t_450);
/* arena_runs_dirty_link_t
/* arena_runs_dirty_link_t_450
* source: https://github.com/jemalloc/jemalloc/blob/4.5.0/include/jemalloc/internal/arena.h
*/
struct GH_ALIGN GH(arena_runs_dirty_link_s) {
struct GH_ALIGN GH(arena_runs_dirty_link_s_450) {
RZ_JM_QR(void) rd_link;
};
/* extent_node_t - chunk/extent tracking
/* extent_node_t_450 - chunk/extent tracking
* source: https://github.com/jemalloc/jemalloc/blob/4.5.0/include/jemalloc/internal/extent.h
*/
struct GH_ALIGN GH(extent_node_s) {
struct GH_ALIGN GH(extent_node_s_450) {
GHT en_arena;
GHT en_addr;
GHT en_size;
@ -122,108 +126,108 @@ struct GH_ALIGN GH(extent_node_s) {
bool en_committed;
bool en_achunk;
GHT en_prof_tctx;
GH(arena_runs_dirty_link_t) rd;
RZ_JM_QR(GH(extent_node_t)) cc_link;
GH(arena_runs_dirty_link_t_450) rd;
RZ_JM_QR(GH(extent_node_t_450)) cc_link;
union {
RZ_JM_RB_NODE(GH(extent_node_t)) szsnad_link;
RZ_JM_QL_ELM(GH(extent_node_t)) ql_link;
RZ_JM_RB_NODE(GH(extent_node_t_450)) szsnad_link;
RZ_JM_QL_ELM(GH(extent_node_t_450)) ql_link;
};
RZ_JM_RB_NODE(GH(extent_node_t)) ad_link;
RZ_JM_RB_NODE(GH(extent_node_t_450)) ad_link;
};
/* arena_decay_t
/* arena_decay_t_450
* source: https://github.com/jemalloc/jemalloc/blob/4.5.0/include/jemalloc/internal/arena.h
*/
struct GH_ALIGN GH(arena_decay_s) {
struct GH_ALIGN GH(arena_decay_s_450) {
GHST time;
GH(nstime_t) interval;
GH(nstime_t) epoch;
GH(nstime_t_450) interval;
GH(nstime_t_450) epoch;
ut64 jitter_state;
GH(nstime_t) deadline;
GH(nstime_t_450) deadline;
GHT ndirty;
GHT backlog[SMOOTHSTEP_NSTEPS];
};
/* arena_run_heap_t - pairing heap of runs
/* arena_run_heap_t_450 - pairing heap of runs
* source: https://github.com/jemalloc/jemalloc/blob/4.5.0/include/jemalloc/internal/arena.h
*/
typedef RZ_JM_PH(void) GH(arena_run_heap_t);
typedef RZ_JM_PH(void) GH(arena_run_heap_t_450);
/* arena_t - main arena structure
/* arena_t_450 - main arena structure
* source: https://github.com/jemalloc/jemalloc/blob/4.5.0/include/jemalloc/internal/arena.h
*/
struct GH_ALIGN GH(arena_s) {
struct GH_ALIGN GH(arena_s_450) {
unsigned ind;
unsigned nthreads[2];
GH(malloc_mutex_t) lock;
GH(arena_stats_t) stats;
GH(malloc_mutex_t_450) lock;
GH(arena_stats_t_450) stats;
RZ_JM_QL_HEAD(void) tcache_ql;
ut64 prof_accumbytes;
GHT offset_state;
dss_prec_t dss_prec;
RZ_JM_QL_HEAD(GH(extent_node_t)) achunks;
RZ_JM_QL_HEAD(GH(extent_node_t_450)) achunks;
GHT extent_sn_next;
GHT spare;
GHST lg_dirty_mult;
bool purging;
GHT nactive;
GHT ndirty;
GH(arena_runs_dirty_link_t) runs_dirty;
GH(extent_node_t) chunks_cache;
GH(arena_decay_t) decay;
RZ_JM_QL_HEAD(GH(extent_node_t)) huge;
GH(malloc_mutex_t) huge_mtx;
GH(extent_tree_t) chunks_szsnad_cached;
GH(extent_tree_t) chunks_ad_cached;
GH(extent_tree_t) chunks_szsnad_retained;
GH(extent_tree_t) chunks_ad_retained;
GH(malloc_mutex_t) chunks_mtx;
RZ_JM_QL_HEAD(GH(extent_node_t)) node_cache;
GH(malloc_mutex_t) node_cache_mtx;
GH(chunk_hooks_t) chunk_hooks;
GH(arena_bin_t) bins[JM_NBINS];
GH(arena_run_heap_t) runs_avail[GH(NPSIZES)];
GH(arena_runs_dirty_link_t_450) runs_dirty;
GH(extent_node_t_450) chunks_cache;
GH(arena_decay_t_450) decay;
RZ_JM_QL_HEAD(GH(extent_node_t_450)) huge;
GH(malloc_mutex_t_450) huge_mtx;
GH(extent_tree_t_450) chunks_szsnad_cached;
GH(extent_tree_t_450) chunks_ad_cached;
GH(extent_tree_t_450) chunks_szsnad_retained;
GH(extent_tree_t_450) chunks_ad_retained;
GH(malloc_mutex_t_450) chunks_mtx;
RZ_JM_QL_HEAD(GH(extent_node_t_450)) node_cache;
GH(malloc_mutex_t_450) node_cache_mtx;
GH(chunk_hooks_t_450) chunk_hooks;
GH(arena_bin_t_450) bins[JM_NBINS];
GH(arena_run_heap_t_450) runs_avail[GH(NPSIZES)];
};
/* bitmap_level_t
/* bitmap_level_t_450
* source: https://github.com/jemalloc/jemalloc/blob/4.5.0/include/jemalloc/internal/bitmap.h
*/
struct GH_ALIGN GH(bitmap_level_s) {
struct GH_ALIGN GH(bitmap_level_s_450) {
GHT group_offset;
};
/* bitmap_info_t
/* bitmap_info_t_450
* source: https://github.com/jemalloc/jemalloc/blob/4.5.0/include/jemalloc/internal/bitmap.h
*/
#ifndef GH_IS_64
/* 32-bit uses USE_TREE */
struct GH_ALIGN GH(bitmap_info_s) {
struct GH_ALIGN GH(bitmap_info_s_450) {
GHT nbits;
unsigned nlevels;
GH(bitmap_level_t) levels[BITMAP_MAX_LEVELS+1];
GH(bitmap_level_t_450) levels[BITMAP_MAX_LEVELS+1];
};
#else
/* 64-bit does not use USE_TREE */
struct GH_ALIGN GH(bitmap_info_s) {
struct GH_ALIGN GH(bitmap_info_s_450) {
GHT nbits;
GHT ngroups;
};
#endif
/* arena_bin_info_t
/* arena_bin_info_t_450
* source: https://github.com/jemalloc/jemalloc/blob/4.5.0/include/jemalloc/internal/arena.h
*/
struct GH_ALIGN GH(arena_bin_info_s) {
struct GH_ALIGN GH(arena_bin_info_s_450) {
GHT reg_size;
GHT redzone_size;
GHT reg_interval;
GHT run_size;
ut32 nregs;
GH(bitmap_info_t) bitmap_info;
GH(bitmap_info_t_450) bitmap_info;
ut32 reg0_offset;
};

View file

@ -0,0 +1,550 @@
// SPDX-FileCopyrightText: 2026 jemalloc <https://jemalloc.net/>
// SPDX-FileCopyrightText: 2026 bubblepipe <bubblepipe42@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#ifndef RZ_JEMALLOC_530_H
#define RZ_JEMALLOC_530_H
#include <rz_util.h>
#include <rz_io.h>
#define EDATA_SIZE_64 128
#define EDATA_SIZE_32 108
#define EDATA_SIZE_MAX 128
#define BIN_INFO_SIZE_64 40
#define BIN_INFO_SIZE_32 48
#define BIN_INFO_SIZE_MAX 48
#define BIN_SIZE_64 224
#define BIN_SIZE_32 172
#define BIN_SIZE_MAX 224
#define ARENA_SIZE_64 78952
#define ARENA_SIZE_32 22004
#define ARENA_SIZE_MAX 78952
#define RTREE_NODE_ELM_SIZE_64 8
#define RTREE_NODE_ELM_SIZE_32 4
#define RTREE_NODE_ELM_SIZE_MAX 8
#define RTREE_LEAF_ELM_SIZE_64 8
#define RTREE_LEAF_ELM_SIZE_32 8
#define RTREE_LEAF_ELM_SIZE_MAX 8
#define PHN_LINK_SIZE_64 24
#define PHN_LINK_SIZE_32 12
#define PH_SIZE_64 16
#define PH_SIZE_32 8
#define NSTIME_SIZE_64 8
#define NSTIME_SIZE_32 8
#define BITMAP_INFO_SIZE_64 16
#define BITMAP_INFO_SIZE_32 32
#define RTREE_SIZE_64 2097272
#define RTREE_SIZE_32 4188
#define SC_NSIZES 235
#define SC_LG_SLAB_MAXREGS 9
#define MALLOC_MUTEX_SIZE_64 112
#define MALLOC_MUTEX_SIZE_32 88
#define BIN_STATS_SIZE_64 80
#define BIN_STATS_SIZE_32 68
#define MALLOCX_ARENA_BITS 12
#define EDATA_ALIGNMENT 128
#define LG_PAGE_530 12
#define JM_NBINS_530 36
#define ARENA_LAST_THD_OFFSET_64 16
#define ARENA_STATS_OFFSET_64 24
#define ARENA_TCACHE_QL_OFFSET_64 10392
#define ARENA_CACHE_BIN_ARR_OFFSET_64 10400
#define ARENA_TCACHE_QL_MTX_OFFSET_64 10408
#define ARENA_DSS_PREC_OFFSET_64 10520
#define ARENA_LARGE_OFFSET_64 10528
#define ARENA_LARGE_MTX_OFFSET_64 10536
#define ARENA_PA_SHARD_OFFSET_64 10648
#define ARENA_IND_OFFSET_64 78928
#define ARENA_BASE_OFFSET_64 78936
#define ARENA_CREATE_TIME_OFFSET_64 78944
#define ARENA_BINS_OFFSET_64 78952
#define ARENA_STATS_OFFSET_32 16
#define ARENA_TCACHE_QL_OFFSET_32 3960
#define ARENA_CACHE_BIN_ARR_OFFSET_32 3964
#define ARENA_TCACHE_QL_MTX_OFFSET_32 3968
#define ARENA_LARGE_OFFSET_32 4032
#define ARENA_LARGE_MTX_OFFSET_32 4036
#define ARENA_PA_SHARD_OFFSET_32 4040
#define ARENA_DSS_PREC_OFFSET_32 4056
#define ARENA_IND_OFFSET_32 21988
#define ARENA_BASE_OFFSET_32 21992
#define ARENA_CREATE_TIME_OFFSET_32 21996
#define ARENA_BINS_OFFSET_32 22004
#define BIN_SLABCUR_OFFSET_64 192
#define BIN_SLABCUR_OFFSET_32 156
#define RTREE_NSB_64 36
#define RTREE_NSB_32 20
#define RTREE_BITS_PER_LEVEL_64 18
#define RTREE_BITS_PER_LEVEL_32 10
#define RTREE_MAX_SUBKEYS_64 (1U << RTREE_BITS_PER_LEVEL_64)
#define RTREE_MAX_SUBKEYS_32 (1U << RTREE_BITS_PER_LEVEL_32)
#define RTREE_ROOT_OFFSET_64 120
#define RTREE_ROOT_OFFSET_32 92
#define MASK(CURRENT_FIELD_WIDTH, CURRENT_FIELD_SHIFT) ((((((ut64)0x1U) << (CURRENT_FIELD_WIDTH)) - 1)) << (CURRENT_FIELD_SHIFT))
#define EDATA_BITS_ARENA_WIDTH MALLOCX_ARENA_BITS
#define EDATA_BITS_ARENA_SHIFT 0
#define EDATA_BITS_ARENA_MASK MASK(EDATA_BITS_ARENA_WIDTH, EDATA_BITS_ARENA_SHIFT)
#define EDATA_BITS_SLAB_WIDTH 1
#define EDATA_BITS_SLAB_SHIFT (EDATA_BITS_ARENA_WIDTH + EDATA_BITS_ARENA_SHIFT)
#define EDATA_BITS_SLAB_MASK MASK(EDATA_BITS_SLAB_WIDTH, EDATA_BITS_SLAB_SHIFT)
#define EDATA_BITS_COMMITTED_WIDTH 1
#define EDATA_BITS_COMMITTED_SHIFT (EDATA_BITS_SLAB_WIDTH + EDATA_BITS_SLAB_SHIFT)
#define EDATA_BITS_COMMITTED_MASK MASK(EDATA_BITS_COMMITTED_WIDTH, EDATA_BITS_COMMITTED_SHIFT)
#define EDATA_BITS_PAI_WIDTH 1
#define EDATA_BITS_PAI_SHIFT (EDATA_BITS_COMMITTED_WIDTH + EDATA_BITS_COMMITTED_SHIFT)
#define EDATA_BITS_PAI_MASK MASK(EDATA_BITS_PAI_WIDTH, EDATA_BITS_PAI_SHIFT)
#define EDATA_BITS_ZEROED_WIDTH 1
#define EDATA_BITS_ZEROED_SHIFT (EDATA_BITS_PAI_WIDTH + EDATA_BITS_PAI_SHIFT)
#define EDATA_BITS_ZEROED_MASK MASK(EDATA_BITS_ZEROED_WIDTH, EDATA_BITS_ZEROED_SHIFT)
#define EDATA_BITS_GUARDED_WIDTH 1
#define EDATA_BITS_GUARDED_SHIFT (EDATA_BITS_ZEROED_WIDTH + EDATA_BITS_ZEROED_SHIFT)
#define EDATA_BITS_GUARDED_MASK MASK(EDATA_BITS_GUARDED_WIDTH, EDATA_BITS_GUARDED_SHIFT)
#define EDATA_BITS_STATE_WIDTH 3
#define EDATA_BITS_STATE_SHIFT (EDATA_BITS_GUARDED_WIDTH + EDATA_BITS_GUARDED_SHIFT)
#define EDATA_BITS_STATE_MASK MASK(EDATA_BITS_STATE_WIDTH, EDATA_BITS_STATE_SHIFT)
#define EDATA_BITS_SZIND_WIDTH LG_CEIL(SC_NSIZES)
#define EDATA_BITS_SZIND_SHIFT (EDATA_BITS_STATE_WIDTH + EDATA_BITS_STATE_SHIFT)
#define EDATA_BITS_SZIND_MASK MASK(EDATA_BITS_SZIND_WIDTH, EDATA_BITS_SZIND_SHIFT)
#define EDATA_BITS_NFREE_WIDTH (SC_LG_SLAB_MAXREGS + 1)
#define EDATA_BITS_NFREE_SHIFT (EDATA_BITS_SZIND_WIDTH + EDATA_BITS_SZIND_SHIFT)
#define EDATA_BITS_NFREE_MASK MASK(EDATA_BITS_NFREE_WIDTH, EDATA_BITS_NFREE_SHIFT)
#define EDATA_BITS_BINSHARD_WIDTH 6
#define EDATA_BITS_BINSHARD_SHIFT (EDATA_BITS_NFREE_WIDTH + EDATA_BITS_NFREE_SHIFT)
#define EDATA_BITS_BINSHARD_MASK MASK(EDATA_BITS_BINSHARD_WIDTH, EDATA_BITS_BINSHARD_SHIFT)
#define EDATA_BITS_IS_HEAD_WIDTH 1
#define EDATA_BITS_IS_HEAD_SHIFT (EDATA_BITS_BINSHARD_WIDTH + EDATA_BITS_BINSHARD_SHIFT)
#define EDATA_BITS_IS_HEAD_MASK MASK(EDATA_BITS_IS_HEAD_WIDTH, EDATA_BITS_IS_HEAD_SHIFT)
#define RTREE_LEAF_STATE_WIDTH EDATA_BITS_STATE_WIDTH
#define RTREE_LEAF_STATE_SHIFT 2
#define RTREE_LEAF_STATE_MASK MASK(RTREE_LEAF_STATE_WIDTH, RTREE_LEAF_STATE_SHIFT)
/*
* source: https://github.com/jemalloc/jemalloc/blob/5.3.0/include/jemalloc/internal/ph.h
*/
typedef struct phn_link_t_530 {
ut64 prev;
ut64 next;
ut64 lchild;
} phn_link_t_530;
/*
* source: https://github.com/jemalloc/jemalloc/blob/5.3.0/include/jemalloc/internal/ph.h
*/
typedef struct ph_t_530 {
ut64 root;
ut64 auxcount;
} ph_t_530;
/*
* source: https://github.com/jemalloc/jemalloc/blob/5.3.0/include/jemalloc/internal/rtree.h
*/
typedef struct rtree_leaf_elm_t_530 {
ut64 le_bits; // Used for 64-bit compact format
ut64 le_edata; // Used for 32-bit non-compact format
ut64 le_metadata; // Used for 32-bit non-compact format
} rtree_leaf_elm_t_530;
/*
* source: https://github.com/jemalloc/jemalloc/blob/5.3.0/include/jemalloc/internal/rtree.h
*/
typedef struct rtree_node_elm_t_530 {
ut64 child;
} rtree_node_elm_t_530;
/*
* source: https://github.com/jemalloc/jemalloc/blob/5.3.0/include/jemalloc/internal/edata.h
*/
typedef struct edata_t_530 {
ut64 e_bits;
ut64 e_addr;
ut64 e_size_esn; // union: e_size_esn or e_bsize
ut64 e_ps;
ut64 e_sn;
} edata_t_530;
/*
* source: https://github.com/jemalloc/jemalloc/blob/5.3.0/include/jemalloc/internal/bin.h
*/
typedef struct nstime_t_530 {
ut64 ns;
} nstime_t_530;
/*
* Unified bitmap_info structure
* 64-bit: nbits(8) + ngroups(8) = 16 bytes (no BITMAP_USE_TREE)
* 32-bit: nbits(4) + nlevels(4) + levels[6](24) = 32 bytes (BITMAP_USE_TREE)
*/
typedef struct bitmap_info_t_530 {
ut64 nbits;
// Non-tree format (64-bit)
ut64 ngroups;
// Tree format (32-bit)
ut32 nlevels;
ut64 levels[6]; // BITMAP_MAX_LEVELS + 1
} bitmap_info_t_530;
/*
* source: https://github.com/jemalloc/jemalloc/blob/5.3.0/include/jemalloc/internal/bin.h
*/
typedef struct bin_info_t_530 {
ut64 reg_size;
ut64 slab_size;
ut32 nregs;
ut32 n_shards;
bitmap_info_t_530 bitmap_info;
} bin_info_t_530;
/*
* source: https://github.com/jemalloc/jemalloc/blob/5.3.0/include/jemalloc/internal/bin.h
*/
typedef struct bin_t_530 {
ut64 slabcur;
} bin_t_530;
/*
* source: https://github.com/jemalloc/jemalloc/blob/5.3.0/include/jemalloc/internal/arena_structs.h
*/
typedef struct arena_t_530 {
ut32 nthreads[2];
ut32 binshard_next;
ut64 last_thd;
ut32 dss_prec;
ut32 ind;
ut64 base;
ut64 create_time_ns;
ut64 stats_addr;
ut64 tcache_ql_addr;
ut64 cache_bin_array_descriptor_ql_addr;
ut64 tcache_ql_mtx_addr;
ut64 large_addr;
ut64 large_mtx_addr;
ut64 pa_shard_addr;
ut64 bins_addr;
} arena_t_530;
static inline size_t rtree_node_elm_size_530(bool is_64bit) {
return is_64bit ? RTREE_NODE_ELM_SIZE_64 : RTREE_NODE_ELM_SIZE_32;
}
static inline size_t rtree_leaf_elm_size_530(bool is_64bit) {
return is_64bit ? RTREE_LEAF_ELM_SIZE_64 : RTREE_LEAF_ELM_SIZE_32;
}
static inline size_t edata_size_530(bool is_64bit) {
return is_64bit ? EDATA_SIZE_64 : EDATA_SIZE_32;
}
static inline size_t bin_info_size_530(bool is_64bit) {
return is_64bit ? BIN_INFO_SIZE_64 : BIN_INFO_SIZE_32;
}
static inline size_t bin_size_530(bool is_64bit) {
return is_64bit ? BIN_SIZE_64 : BIN_SIZE_32;
}
static inline size_t arena_size_530(bool is_64bit) {
return is_64bit ? ARENA_SIZE_64 : ARENA_SIZE_32;
}
static inline ut64 bins_offset_530(bool is_64bit) {
return is_64bit ? ARENA_BINS_OFFSET_64 : ARENA_BINS_OFFSET_32;
}
static inline bool read_and_parse_rtree_node_elm_t_530(RzIO *io, ut64 addr, rtree_node_elm_t_530 *out, bool is_64bit) {
size_t size = rtree_node_elm_size_530(is_64bit);
ut8 buf[RTREE_NODE_ELM_SIZE_MAX];
if (!rz_io_read_at_mapped(io, addr, buf, size)) {
return false;
}
RzBuffer *b = rz_buf_new_with_bytes(buf, size);
if (!b) {
return false;
}
ut64 offset = 0;
bool ret;
if (is_64bit) {
ret = rz_buf_read_le64_offset(b, &offset, &out->child);
} else {
ut32 val;
ret = rz_buf_read_le32_offset(b, &offset, &val);
if (ret) {
out->child = val;
}
}
rz_buf_free(b);
return ret;
}
static inline bool read_and_parse_rtree_leaf_elm_t_530(RzIO *io, ut64 addr, rtree_leaf_elm_t_530 *out, bool is_64bit) {
size_t size = rtree_leaf_elm_size_530(is_64bit);
ut8 buf[RTREE_LEAF_ELM_SIZE_MAX];
if (!rz_io_read_at_mapped(io, addr, buf, size)) {
return false;
}
RzBuffer *b = rz_buf_new_with_bytes(buf, size);
if (!b) {
return false;
}
ut64 offset = 0;
bool ret;
if (is_64bit) {
// 64-bit uses compact format with le_bits
out->le_edata = 0;
out->le_metadata = 0;
ret = rz_buf_read_le64_offset(b, &offset, &out->le_bits);
} else {
// 32-bit uses non-compact format
ut32 edata, metadata;
out->le_bits = 0;
ret = rz_buf_read_le32_offset(b, &offset, &edata) &&
rz_buf_read_le32_offset(b, &offset, &metadata);
if (ret) {
out->le_edata = edata;
out->le_metadata = metadata;
}
}
rz_buf_free(b);
return ret;
}
static inline bool read_and_parse_edata_t_530(RzIO *io, ut64 addr, edata_t_530 *out, bool is_64bit) {
size_t size = edata_size_530(is_64bit);
ut8 buf[EDATA_SIZE_MAX];
if (!rz_io_read_at_mapped(io, addr, buf, size)) {
return false;
}
RzBuffer *b = rz_buf_new_with_bytes(buf, size);
if (!b) {
return false;
}
ut64 offset = 0;
bool ret = false;
if (!rz_buf_read_le64_offset(b, &offset, &out->e_bits)) {
rz_buf_free(b);
return ret;
}
if (is_64bit) {
ret = rz_buf_read_le64_offset(b, &offset, &out->e_addr) &&
rz_buf_read_le64_offset(b, &offset, &out->e_size_esn) &&
rz_buf_read_le64_offset(b, &offset, &out->e_ps) &&
rz_buf_read_le64_offset(b, &offset, &out->e_sn);
} else {
ut32 e_addr, size_esn, ps;
if (rz_buf_read_le32_offset(b, &offset, &e_addr) &&
rz_buf_read_le32_offset(b, &offset, &size_esn) &&
rz_buf_read_le32_offset(b, &offset, &ps)) {
out->e_addr = e_addr;
out->e_size_esn = size_esn;
out->e_ps = ps;
ret = rz_buf_read_le64_offset(b, &offset, &out->e_sn);
}
}
rz_buf_free(b);
return ret;
}
static inline bool read_and_parse_bin_info_t_530(RzIO *io, ut64 addr, bin_info_t_530 *out, bool is_64bit) {
size_t size = bin_info_size_530(is_64bit);
ut8 buf[BIN_INFO_SIZE_MAX];
if (!rz_io_read_at_mapped(io, addr, buf, size)) {
return false;
}
RzBuffer *b = rz_buf_new_with_bytes(buf, size);
if (!b) {
return false;
}
ut64 offset = 0;
bool ret = false;
if (is_64bit) {
if (rz_buf_read_le64_offset(b, &offset, &out->reg_size) &&
rz_buf_read_le64_offset(b, &offset, &out->slab_size) &&
rz_buf_read_le32_offset(b, &offset, &out->nregs) &&
rz_buf_read_le32_offset(b, &offset, &out->n_shards) &&
rz_buf_read_le64_offset(b, &offset, &out->bitmap_info.nbits) &&
rz_buf_read_le64_offset(b, &offset, &out->bitmap_info.ngroups)) {
out->bitmap_info.nlevels = 0;
ret = true;
}
} else {
ut32 reg_size, slab_size, nbits;
if (rz_buf_read_le32_offset(b, &offset, &reg_size) &&
rz_buf_read_le32_offset(b, &offset, &slab_size) &&
rz_buf_read_le32_offset(b, &offset, &out->nregs) &&
rz_buf_read_le32_offset(b, &offset, &out->n_shards) &&
rz_buf_read_le32_offset(b, &offset, &nbits) &&
rz_buf_read_le32_offset(b, &offset, &out->bitmap_info.nlevels)) {
out->reg_size = reg_size;
out->slab_size = slab_size;
out->bitmap_info.nbits = nbits;
out->bitmap_info.ngroups = 0;
ret = true;
for (int i = 0; i < 6; i++) {
ut32 level;
if (!rz_buf_read_le32_offset(b, &offset, &level)) {
ret = false;
break;
}
out->bitmap_info.levels[i] = level;
}
}
}
rz_buf_free(b);
return ret;
}
static inline bool read_and_parse_bin_t_530(RzIO *io, ut64 addr, bin_t_530 *out, bool is_64bit) {
size_t size = bin_size_530(is_64bit);
ut8 buf[BIN_SIZE_MAX];
if (!rz_io_read_at_mapped(io, addr, buf, size)) {
return false;
}
RzBuffer *b = rz_buf_new_with_bytes(buf, size);
if (!b) {
return false;
}
ut64 offset;
bool ret;
if (is_64bit) {
offset = BIN_SLABCUR_OFFSET_64;
ret = rz_buf_read_le64_offset(b, &offset, &out->slabcur);
} else {
offset = BIN_SLABCUR_OFFSET_32;
ut32 slabcur;
ret = rz_buf_read_le32_offset(b, &offset, &slabcur);
if (ret) {
out->slabcur = slabcur;
}
}
rz_buf_free(b);
return ret;
}
static inline bool read_and_parse_arena_t_530(RzIO *io, ut64 addr, arena_t_530 *out, bool is_64bit) {
size_t size = arena_size_530(is_64bit);
ut8 *buf = malloc(size);
if (!buf) {
return false;
}
if (!rz_io_read_at_mapped(io, addr, buf, size)) {
free(buf);
return false;
}
RzBuffer *b = rz_buf_new_with_bytes(buf, size);
free(buf);
if (!b) {
return false;
}
ut64 offset = 0;
bool ret = false;
if (!rz_buf_read_le32_offset(b, &offset, &out->nthreads[0]) ||
!rz_buf_read_le32_offset(b, &offset, &out->nthreads[1]) ||
!rz_buf_read_le32_offset(b, &offset, &out->binshard_next)) {
rz_buf_free(b);
return ret;
}
if (is_64bit) {
offset = ARENA_LAST_THD_OFFSET_64;
if (rz_buf_read_le64_offset(b, &offset, &out->last_thd)) {
offset = ARENA_DSS_PREC_OFFSET_64;
if (rz_buf_read_le32_offset(b, &offset, &out->dss_prec)) {
offset = ARENA_IND_OFFSET_64;
if (rz_buf_read_le32_offset(b, &offset, &out->ind)) {
offset = ARENA_BASE_OFFSET_64;
if (rz_buf_read_le64_offset(b, &offset, &out->base) &&
rz_buf_read_le64_offset(b, &offset, &out->create_time_ns)) {
out->stats_addr = addr + ARENA_STATS_OFFSET_64;
out->tcache_ql_addr = addr + ARENA_TCACHE_QL_OFFSET_64;
out->cache_bin_array_descriptor_ql_addr = addr + ARENA_CACHE_BIN_ARR_OFFSET_64;
out->tcache_ql_mtx_addr = addr + ARENA_TCACHE_QL_MTX_OFFSET_64;
out->large_addr = addr + ARENA_LARGE_OFFSET_64;
out->large_mtx_addr = addr + ARENA_LARGE_MTX_OFFSET_64;
out->pa_shard_addr = addr + ARENA_PA_SHARD_OFFSET_64;
out->bins_addr = addr + ARENA_BINS_OFFSET_64;
ret = true;
}
}
}
}
} else {
ut32 last_thd;
if (rz_buf_read_le32_offset(b, &offset, &last_thd)) {
out->last_thd = last_thd;
offset = ARENA_DSS_PREC_OFFSET_32;
if (rz_buf_read_le32_offset(b, &offset, &out->dss_prec)) {
offset = ARENA_IND_OFFSET_32;
ut32 base;
if (rz_buf_read_le32_offset(b, &offset, &out->ind) &&
rz_buf_read_le32_offset(b, &offset, &base)) {
out->base = base;
if (rz_buf_read_le64_offset(b, &offset, &out->create_time_ns)) {
out->stats_addr = addr + ARENA_STATS_OFFSET_32;
out->tcache_ql_addr = addr + ARENA_TCACHE_QL_OFFSET_32;
out->cache_bin_array_descriptor_ql_addr = addr + ARENA_CACHE_BIN_ARR_OFFSET_32;
out->tcache_ql_mtx_addr = addr + ARENA_TCACHE_QL_MTX_OFFSET_32;
out->large_addr = addr + ARENA_LARGE_OFFSET_32;
out->large_mtx_addr = addr + ARENA_LARGE_MTX_OFFSET_32;
out->pa_shard_addr = addr + ARENA_PA_SHARD_OFFSET_32;
out->bins_addr = addr + ARENA_BINS_OFFSET_32;
ret = true;
}
}
}
}
}
rz_buf_free(b);
return ret;
}
static inline ut64 rtree_leaf_elm_bits_edata_get_530(ut64 bits) {
if (bits == 0) {
return 0;
}
ut32 rtree_nhib = 16;
/* pwndbg algorithm:
* ls = (val << RTREE_NHIB) & ((2**64) - 1)
* ptr = ((ls >> RTREE_NHIB) >> 1) << 1
* ptr = ptr & ~(EDATA_ALIGNMENT - 1) // align to 128 bytes
*/
ut64 ls = bits << rtree_nhib;
ut64 ptr = ((ls >> rtree_nhib) >> 1) << 1;
ptr = ptr & ~((ut64)128 - 1);
return ptr;
}
static inline void rtree_params_530(bool is_64bit, ut32 *lg_page, ut32 *rtree_nsb,
ut32 *bits_per_level, ut32 *max_subkeys,
ut64 *root_offset) {
*lg_page = LG_PAGE_530;
if (is_64bit) {
*rtree_nsb = RTREE_NSB_64;
*bits_per_level = RTREE_BITS_PER_LEVEL_64;
*max_subkeys = RTREE_MAX_SUBKEYS_64;
*root_offset = RTREE_ROOT_OFFSET_64;
} else {
*rtree_nsb = RTREE_NSB_32;
*bits_per_level = RTREE_BITS_PER_LEVEL_32;
*max_subkeys = RTREE_MAX_SUBKEYS_32;
*root_offset = RTREE_ROOT_OFFSET_32;
}
}
#endif // RZ_JEMALLOC_530_H

View file

@ -1,8 +1,10 @@
NAME=dmxa/dmxb with jemalloc memory dump
NAME=dmxa/dmxb with jemalloc 4.5.0 memory dump
FILE=bins/heap/linux_jemalloc-4.5.0_x64.bin
ARGS=-n
CMDS=<<EOF
e asm.bits=64
e dbg.jemalloc.version=4.5.0
om 3 0x7f2601a03c00 0x8 0x0 rw- je_arenas
om 3 0x7f2601f5b2d0 0x4 0x8 rw- narenas_total
om 3 0x7f2601f5b380 0x900 0xc rw- je_arena_bin_info
@ -37,3 +39,88 @@ EXPECT=<<EOF
nregs : 0x200
EOF
RUN
NAME=dmxa/dmxb/dmxei with jemalloc 5.3.0 memory dump
FILE=bins/heap/linux_jemalloc-5.3.0_x64.bin
ARGS=-n
CMDS=<<EOF
e asm.bits=64
e dbg.jemalloc.version=5.3.0
om 3 0x561974e68b00 1440 0x0 rw- bin_info
om 3 0x7faa2cc010c0 0x20000 1440 rw- arena
om 3 0x7faa2cc16500 128 0x205a0 rw- extent
dmxa 0x7faa2cc010c0~ind
dmxa 0x7faa2cc010c0~nthreads: application
dmxa 0x7faa2cc010c0~stats
dmxa 0x7faa2cc010c0~dss_prec
dmxa 0x7faa2cc010c0~create_time.ns
echo ----
dmxb 0x7faa2cc010c0 0x561974e68b00~?reg_size
dmxb 0x7faa2cc010c0 0x561974e68b00~reg_size : 0x800
dmxb 0x7faa2cc010c0 0x561974e68b00~reg_size : 0x3800
dmxb 0x7faa2cc010c0 0x561974e68b00~nregs : 0x200
echo ----
dmxei 0x7faa2cc16500~Extent
dmxei 0x7faa2cc16500~Allocated Address
EOF
EXPECT=<<EOF
ind = 0x0
nthreads: application allocation = 0x1
stats = 0x7faa2cc010d8
dss_prec = 0x2
create_time.ns = 0x1a488a2868dea
----
36
reg_size : 0x800
reg_size : 0x3800
nregs : 0x200
----
Extent @ 0x7faa2cc16500
Allocated Address: 0x7faa2ca00000
EOF
RUN
NAME=dmxa/dmxb/dmxe with 32-bit jemalloc 5.3.0 runtime
FILE=bins/heap/simpleheap-jemalloc-5.3.0-static-x32
ARGS=-d
CMDS=<<EOF
dcu sym.imp.getchar
dmxa~?narenas
echo ----
dmxb~reg_size : 0x800
echo ----
dmxe~?Total extents found:
EOF
EXPECT=<<EOF
1
----
reg_size : 0x800
----
1
EOF
RUN
NAME=dmxa/dmxb/dmxe with 64-bit jemalloc 5.3.0 runtime
FILE=bins/heap/simpleheap-jemalloc-5.3.0-static-x64
ARGS=-d
CMDS=<<EOF
dcu sym.imp.getchar
dmxa~?narenas
echo ----
dmxb~reg_size : 0x800
echo ----
dmxe~Total extents found:
EOF
EXPECT=<<EOF
1
----
reg_size : 0x800
----
Total extents found: 16
EOF
RUN

View file

@ -168,6 +168,7 @@ if get_option('enable_tests')
rz_magic_dep,
rz_il_dep,
rz_mark_dep,
dependency('rzheap'),
lrt,
],
install: false,