From 2ee0d4a42b64ccbe7a571c626c4433d4ca1821bf Mon Sep 17 00:00:00 2001 From: bubblepipe Date: Sun, 18 Jan 2026 16:44:57 +0800 Subject: [PATCH] heap: add support for jemalloc v5.3.0 heap parsing (#5744) --- librz/core/cconfig.c | 4 + librz/core/cmd/cmd_debug.c | 10 + librz/core/cmd_descs/cmd_debug.yaml | 23 +- librz/core/cmd_descs/cmd_descs.c | 45 +- librz/core/cmd_descs/cmd_descs.h | 4 + librz/core/linux_heap_jemalloc.c | 762 ++++++++++++++---- librz/include/rz_heap_jemalloc.h | 6 +- .../{jemalloc.h => jemalloc_450.h} | 156 ++-- subprojects/rzheap/rz_jemalloc/jemalloc_530.h | 550 +++++++++++++ test/db/archos/linux-x64/dbg_dmx | 89 +- test/unit/meson.build | 1 + 11 files changed, 1407 insertions(+), 243 deletions(-) rename subprojects/rzheap/rz_jemalloc/{jemalloc.h => jemalloc_450.h} (51%) create mode 100644 subprojects/rzheap/rz_jemalloc/jemalloc_530.h diff --git a/librz/core/cconfig.c b/librz/core/cconfig.c index c1b98fc8bc..6533f3b80f 100644 --- a/librz/core/cconfig.c +++ b/librz/core/cconfig.c @@ -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)"); diff --git a/librz/core/cmd/cmd_debug.c b/librz/core/cmd/cmd_debug.c index 279920f723..1afc6127e7 100644 --- a/librz/core/cmd/cmd_debug.c +++ b/librz/core/cmd/cmd_debug.c @@ -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 /**/ *frames) { RzDebugFrame *f; RzListIter *iter; diff --git a/librz/core/cmd_descs/cmd_debug.yaml b/librz/core/cmd_descs/cmd_debug.yaml index 084d7643b4..b00b280d7e 100644 --- a/librz/core/cmd_descs/cmd_debug.yaml +++ b/librz/core/cmd_descs/cmd_debug.yaml @@ -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: diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index 1201567449..7eccb67cc3 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -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); diff --git a/librz/core/cmd_descs/cmd_descs.h b/librz/core/cmd_descs/cmd_descs.h index 003511a0cc..4594046c6f 100644 --- a/librz/core/cmd_descs/cmd_descs.h +++ b/librz/core/cmd_descs/cmd_descs.h @@ -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" diff --git a/librz/core/linux_heap_jemalloc.c b/librz/core/linux_heap_jemalloc.c index d1b51e9f82..288a33cbc5 100644 --- a/librz/core/linux_heap_jemalloc.c +++ b/librz/core/linux_heap_jemalloc.c @@ -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 \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 + 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 \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"); } } diff --git a/librz/include/rz_heap_jemalloc.h b/librz/include/rz_heap_jemalloc.h index f6a1c705f1..d53a047410 100644 --- a/librz/include/rz_heap_jemalloc.h +++ b/librz/include/rz_heap_jemalloc.h @@ -27,7 +27,8 @@ typedef st32 RZ_ALIGNED(4) GHST_32; #define GHST GHST_32 #endif -#include +/* jemalloc 4.5.0 still uses the GH/GHT macro system */ +#include #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 + #undef PRINTF_A #undef PRINTF_YA #undef PRINTF_GA diff --git a/subprojects/rzheap/rz_jemalloc/jemalloc.h b/subprojects/rzheap/rz_jemalloc/jemalloc_450.h similarity index 51% rename from subprojects/rzheap/rz_jemalloc/jemalloc.h rename to subprojects/rzheap/rz_jemalloc/jemalloc_450.h index a041d8bbb8..3233bbd836 100644 --- a/subprojects/rzheap/rz_jemalloc/jemalloc.h +++ b/subprojects/rzheap/rz_jemalloc/jemalloc_450.h @@ -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; }; diff --git a/subprojects/rzheap/rz_jemalloc/jemalloc_530.h b/subprojects/rzheap/rz_jemalloc/jemalloc_530.h new file mode 100644 index 0000000000..9eb6b2b389 --- /dev/null +++ b/subprojects/rzheap/rz_jemalloc/jemalloc_530.h @@ -0,0 +1,550 @@ +// SPDX-FileCopyrightText: 2026 jemalloc +// SPDX-FileCopyrightText: 2026 bubblepipe +// SPDX-License-Identifier: LGPL-3.0-only + +#ifndef RZ_JEMALLOC_530_H +#define RZ_JEMALLOC_530_H + +#include +#include + +#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, ®_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 diff --git a/test/db/archos/linux-x64/dbg_dmx b/test/db/archos/linux-x64/dbg_dmx index d22b108b30..d9adabe7be 100644 --- a/test/db/archos/linux-x64/dbg_dmx +++ b/test/db/archos/linux-x64/dbg_dmx @@ -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=<