Fix slp address location, honor io.va, baddr and construction instruction

This commit is contained in:
pancake committed 2026-10-08 09:46:53 +02:00
1 parent e073f29cb3
commit 6a243fbc1e
5 files changed
+89 -54

No files matched your search

+6
View File
@@ -87,6 +87,12 @@ Usage: r2hermes[-arg] # see also pd:h for decompilation
r2hermes-S[jr?] - emit SBOM from SLP literals (j=CycloneDX JSON, r=raw input)
```
`r2hermes-L` lists the constructor instruction addresses after `xrefs=`.
Object literal addresses point to their serialized values; their keys or shapes
can be shared by objects with different values. Pools can also share values, so
`r2hermes-Lx <addr>` lists every matching entry. Use the constructor addresses
on each row to navigate to that specific object. Text output follows `io.va`.
### Examples
```bash
+1 -1
View File
@@ -40,7 +40,7 @@ typedef struct {
u32 num_items;
u32 primary_id; /* array_id (array) or keys/shape id (object) */
u32 secondary_id; /* values_id for objects, 0 for arrays */
u32 paddr; /* absolute file offset of the literal data in the SLP pool (0 if unknown) */
u32 paddr; /* absolute file offset of array data or object values (0 if unknown) */
char *formatted; /* cached JS literal text; NULL if not yet formatted */
u32 *xref_addrs; /* instruction addresses that construct this literal */
u32 xref_count;
+5 -14
View File
@@ -133,9 +133,8 @@ static HBCLiteralEntry *cache_find(HBCLiteralCache *c, HBCLiteralKind kind, u32
return NULL;
}
/* Compute paddr for an entry based on its kind/primary_id. For v97+ objects,
* primary_id is a shape index — resolve via the shape table. */
static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id) {
/* Anchor objects at their values, not their shared keys/shape. */
static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id, u32 secondary_id) {
const HBCReader *r = &hbc->reader;
if (kind == HBC_LIT_ARRAY) {
if (!r->arrays) {
@@ -146,18 +145,10 @@ static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id) {
}
return r->arrays_paddr + primary_id;
}
/* object */
if (r->header.version >= 97 && r->object_shapes && primary_id < r->object_shape_count) {
u32 key_off = r->object_shapes[primary_id].key_buffer_offset;
if (r->object_keys && key_off < r->header.objKeyBufferSize) {
return r->object_keys_paddr + key_off;
}
if (!r->object_values || secondary_id >= r->header.objValueBufferSize) {
return 0;
}
if (!r->object_keys || primary_id >= r->header.objKeyBufferSize) {
return 0;
}
return r->object_keys_paddr + primary_id;
return r->object_values_paddr + secondary_id;
}
static Result cache_get_or_create(HBC *hbc, HBCLiteralKind kind, u32 num_items, u32 primary_id, u32 secondary_id, bool format, HBCLiteralEntry **out_entry) {
@@ -182,7 +173,7 @@ static Result cache_get_or_create(HBC *hbc, HBCLiteralKind kind, u32 num_items,
e->num_items = num_items;
e->primary_id = primary_id;
e->secondary_id = secondary_id;
e->paddr = entry_paddr (hbc, kind, primary_id);
e->paddr = entry_paddr (hbc, kind, primary_id, secondary_id);
if (format) {
char *txt = NULL;
Result fr = format_raw_impl (&hbc->reader, kind, num_items, primary_id, secondary_id, &txt);
+32 -38
View File
@@ -741,7 +741,8 @@ static void register_r2_artifacts(RCore *core, const HBCLiteralEntry *e) {
if (!e->paddr) {
return;
}
ut64 vaddr = HBC_VADDR_BASE + (ut64)e->paddr;
ut64 base = r_config_get_b (core->config, "io.va")? HBC_VADDR_BASE: 0;
ut64 vaddr = base + e->paddr;
char flag_name[64];
snprintf (flag_name, sizeof (flag_name), "%s0x%x", lit_kind_prefix (e->kind), e->paddr);
r_flag_set (core->flags, flag_name, vaddr, 1);
@@ -749,7 +750,7 @@ static void register_r2_artifacts(RCore *core, const HBCLiteralEntry *e) {
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, vaddr, e->formatted);
}
for (u32 i = 0; i < e->xref_count; i++) {
ut64 from = HBC_VADDR_BASE + (ut64)e->xref_addrs[i];
ut64 from = base + e->xref_addrs[i];
r_anal_xrefs_set (core->anal, from, vaddr, R_ANAL_REF_TYPE_DATA);
}
}
@@ -812,9 +813,10 @@ static void cmd_lit_list_quiet(HbcContext *ctx, RCore *core) {
if (r.code != RESULT_SUCCESS) {
R_LOG_ERROR ("%s", safe_errmsg (r.error_message));
} else {
ut64 base = r_config_get_b (core->config, "io.va")? HBC_VADDR_BASE: 0;
for (u32 i = 0; i < n; i++) {
const HBCLiteralEntry *e = &arr[i];
const ut64 addr = (ut64)HBC_VADDR_BASE + e->paddr;
const ut64 addr = base + e->paddr;
const char *fmt = e->formatted? e->formatted: "";
r_cons_printf (core->cons, "0x%" PFMT64x " %s\n", addr, fmt);
}
@@ -845,18 +847,16 @@ static void cmd_lit_list_xrefs(HbcContext *ctx, RCore *core, const char *args) {
ut64 want = r_num_get (core->num, args);
ut64 base = va? (ut64)HBC_VADDR_BASE: 0;
ut64 target_paddr = (want >= base)? (want - base): want;
const HBCLiteralEntry *match = NULL;
bool found = false;
for (u32 i = 0; i < n; i++) {
if (arr[i].paddr == target_paddr) {
match = &arr[i];
break;
print_xref_line (core, &arr[i], va);
found = true;
}
}
if (!match) {
if (!found) {
R_LOG_ERROR ("no literal at that address");
return;
}
print_xref_line (core, match, va);
return;
}
for (u32 i = 0; i < n; i++) {
@@ -904,19 +904,23 @@ static void cmd_lit_list(HbcContext *ctx, RCore *core, bool as_json) {
}
r_cons_printf (core->cons, "literals: %u\n", n);
bool va = r_config_get_b (core->config, "io.va");
ut64 base = va? HBC_VADDR_BASE: 0;
for (u32 i = 0; i < n; i++) {
const HBCLiteralEntry *e = &arr[i];
const char *pavastr = va? "vaddr": "paddr";
r_cons_printf (core->cons,
"%-6s n=%-4u id=(%u,%u) %s=0x%08" PFMT64x " xrefs=%u %s\n",
"%-6s n=%-4u id=(%u,%u) %s=0x%08" PFMT64x " xrefs=%u",
kind_label (e->kind),
e->num_items,
e->primary_id,
e->secondary_id,
pavastr,
(ut64)HBC_VADDR_BASE + e->paddr,
e->xref_count,
e->formatted? e->formatted: "");
base + e->paddr,
e->xref_count);
for (u32 j = 0; j < e->xref_count; j++) {
r_cons_printf (core->cons, " 0x%" PFMT64x, base + e->xref_addrs[j]);
}
r_cons_printf (core->cons, " %s\n", e->formatted? e->formatted: "");
}
}
@@ -957,18 +961,16 @@ static void print_r2_for_entry(RCore *core, const HBCLiteralEntry *e, bool va) {
r_cons_printf (core->cons, "f %s0x%x 1 @ 0x%" PFMT64x "\n", prefix, e->paddr, vaddr);
/* embed the formatted text as a comment at the literal vaddr */
if (e->formatted && *e->formatted) {
char *clean = r_str_sanitize_r2 (e->formatted);
if (clean) {
r_str_replace_char (clean, '\n', ' ');
r_str_replace_char (clean, '\r', ' ');
r_cons_printf (core->cons, "\" /* %s */ CC %s @ 0x%" PFMT64x "\n", prefix, clean, vaddr);
free (clean);
char *b64 = r_base64_encode_dyn ((const ut8 *)e->formatted, -1);
if (b64) {
r_cons_printf (core->cons, "CCu base64:%s @ 0x%" PFMT64x "\n", b64, vaddr);
free (b64);
}
}
/* xref from each call site to the literal */
for (u32 j = 0; j < e->xref_count; j++) {
ut64 from = base + (ut64)e->xref_addrs[j];
r_cons_printf (core->cons, "axd 0x%" PFMT64x " 0x%" PFMT64x "\n", from, vaddr);
r_cons_printf (core->cons, "axd 0x%" PFMT64x " 0x%" PFMT64x "\n", vaddr, from);
}
}
@@ -990,18 +992,16 @@ static void cmd_lit_print_r2(HbcContext *ctx, RCore *core, const char *args) {
ut64 want = r_num_get (core->num, args);
ut64 base = va? (ut64)HBC_VADDR_BASE: 0;
ut64 target_paddr = (want >= base)? (want - base): want;
const HBCLiteralEntry *match = NULL;
bool found = false;
for (u32 i = 0; i < n; i++) {
if (arr[i].paddr == target_paddr) {
match = &arr[i];
break;
print_r2_for_entry (core, &arr[i], va);
found = true;
}
}
if (!match) {
if (!found) {
R_LOG_ERROR ("no literal at that address");
return;
}
print_r2_for_entry (core, match, va);
return;
}
for (u32 i = 0; i < n; i++) {
@@ -1058,20 +1058,19 @@ static void cmd_lit_toggle_inline(RCore *core) {
/* Show help */
static const char r2hermes_helpmsg[] =
"Usage: r2hermes-L[subcmd]\n"
" r2hermes-L List cached literals (auto-scans code if cache is empty)\n"
" r2hermes-L List cached literals and constructor addresses (auto-scans if empty)\n"
" r2hermes-Lj List as JSON (auto-scans if needed)\n"
" r2hermes-Lq List as `<vaddr> <formatted>` per line (one per literal)\n"
" r2hermes-Lx [addr] List as `<vaddr> <xref1> <xref2> ...` (one per literal)\n"
" With addr: print xrefs for that single literal only\n"
" r2hermes-Lq List as `<addr> <formatted>` per line (addresses use io.va)\n"
" r2hermes-Lx [addr] List as `<addr> <xref1> <xref2> ...` (addresses use io.va)\n"
" With addr: print all literals sharing that pool address\n"
" r2hermes-Lp[ao] Scan SLP pool (default: arrays; a=arrays, o=objects)\n"
" r2hermes-LR Reset literal cache (does not remove r2 flags/comments)\n"
" r2hermes-Lr [addr] Print r2 oneliners (f/CC/axd) to register flag+comment+xrefs\n"
" With addr: print oneliners for that single literal only\n"
" With addr: print all literals sharing that pool address\n"
" r2hermes-Lg <k> <n> <primary> [<sec>]\n"
" Format a literal from raw params (k=a|o)\n"
" r2hermes-Li Toggle inline literal comments in disasm\n"
" r2hermes-L? This help\n"
"\nDeprecated alias: pd:hL[subcmd]\n";
" r2hermes-L? This help\n";
static void r2hermes_help(RCore *core) {
const char msg[] =
@@ -1099,7 +1098,6 @@ static void cmd_help(RCore *core) {
" pd:ho [id] - Decompile with offsets (addresses) per statement\n"
" pd:hoa - Decompile all with offsets\n"
" pd:hs [id] - List source-line information\n"
" pd:hL[?] - Deprecated alias for r2hermes-L[?]\n"
" pd:h? - Show this help\n"
"\nNote: r2 comments (CC command) are automatically inlined in decompiler output.\n");
}
@@ -1227,10 +1225,6 @@ static void cmd_pdh(RCore *core, HbcContext *ctx, const char *arg) {
}
break;
}
case 'L': /* pd:hL* — deprecated alias for r2hermes-L* */
R_LOG_WARN ("pd:hL is deprecated; use r2hermes-L instead");
cmd_literals (ctx, core, arg + 1);
break;
case 's': /* pd:hs [id] */
cmd_source_lines (ctx, core, arg + 1);
break;
+45 -1
View File
@@ -209,9 +209,53 @@ static int test_decode_rejects_bad_overflow_string_index(void) {
return 0;
}
static int test_literal_addresses(void) {
u8 keys[] = { 0x71, 1, 0, 0, 0 };
u8 values[] = { 0x71, 20, 0, 0, 0, 0x71, 36, 0, 0, 0 };
ShapeTableEntry shape = { .key_buffer_offset = 0, .prop_count = 1 };
for (u32 version = 96; version <= 97; version++) {
HBC hbc = { 0 };
RVecHBCLiteralEntry_init (&hbc.lit_cache.entries);
hbc.reader.header.version = version;
hbc.reader.header.arrayBufferSize = sizeof (values);
hbc.reader.header.objKeyBufferSize = sizeof (keys);
hbc.reader.header.objValueBufferSize = sizeof (values);
hbc.reader.arrays = values;
hbc.reader.arrays_paddr = 0x100;
hbc.reader.object_keys = keys;
hbc.reader.object_keys_paddr = 0x200;
hbc.reader.object_values = values;
hbc.reader.object_values_paddr = version >= 97? 0x100: 0x300;
hbc.reader.object_shapes = &shape;
hbc.reader.object_shape_count = 1;
u32 num_items = version >= 97? 0: 1;
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 0, 0x400).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 5, 0x410).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 5, 0x420).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_ARRAY, 1, 5, 0, 0x430).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, sizeof (values), 0x440).code == RESULT_SUCCESS);
const HBCLiteralEntry *entries = NULL;
u32 count = 0;
CHECK (hbc_literals_list (&hbc, &entries, &count).code == RESULT_SUCCESS);
CHECK (count == 4);
CHECK (entries[0].paddr == hbc.reader.object_values_paddr);
CHECK (entries[1].paddr == hbc.reader.object_values_paddr + 5);
CHECK (entries[2].paddr == 0x105);
CHECK (entries[3].paddr == 0);
CHECK (!strcmp (entries[0].formatted, "{1: 20}"));
CHECK (!strcmp (entries[1].formatted, "{1: 36}"));
CHECK (!strcmp (entries[2].formatted, "[36]"));
CHECK (entries[1].xref_count == 2);
CHECK (entries[1].xref_addrs[0] == 0x410);
CHECK (entries[1].xref_addrs[1] == 0x420);
hbc_literals_reset (&hbc);
}
return 0;
}
int main(int argc, char **argv) {
const char *root = argc > 1? argv[1]: ".";
if (test_small_debug_info (root) || test_empty_debug_info (root) || test_exception_handlers (root) || test_opcode_operand_meanings () || test_function_bytecode_bounds (root) || test_decode_rejects_bad_overflow_string_index ()) {
if (test_small_debug_info (root) || test_empty_debug_info (root) || test_exception_handlers (root) || test_opcode_operand_meanings () || test_function_bytecode_bounds (root) || test_decode_rejects_bad_overflow_string_index () || test_literal_addresses ()) {
return 1;
}
return 0;