Compare commits

...
32 Commits
Author SHA1 Message Date
pancake 911c520da5 Don't define radare_plugin when built into libr via XPS 2026-11-11 01:38:37 +01:00
pancake 5d77df7091 Release 1.6.2 2026-11-06 01:26:23 +01:00
pancake 722793f084 Fix -Wsign-compare in decompiler output truncation check 2026-11-05 12:51:12 +01:00
pancake 0b53e148fb Request trycatch script output in tests 2026-10-20 08:31:28 +02:00
pancake a93ca6767d Release 1.6.0 2026-10-19 17:29:47 +02:00
pancake e3f5b7e560 Use r2 6.2.4 in the ci 2026-10-19 17:29:14 +02:00
pancake 47279d355c Pin the actions 2026-10-19 17:28:11 +02:00
Abhi 3b08970ce8 Fix bindist triplet naming on runners with older r2pm
The 1.5.2 release shipped r2hermes-6.2.2---.zip for unix targets:
the bindist job pins radare2 6.2.2 whose r2pm lacks R2PM_OS/ARCH/BITS.
Fall back to uname (darwin/linux, x86/arm) so all targets get proper
triplet names.
2026-10-18 11:18:58 +05:30
pancake 0566ca00a8 Last minute fix for a missing string flag 2026-10-08 09:57:37 +02:00
pancake 4bfde1f33f Add -A to sync with r2* usage 2026-10-08 09:54:39 +02:00
pancake 6a243fbc1e Fix slp address location, honor io.va, baddr and construction instruction 2026-10-08 09:46:53 +02:00
pancake e073f29cb3 Register as r2hermes pkg 2026-10-06 16:55:28 +02:00
pancake e7bbd96fc7 Release 1.5.2 2026-10-06 00:44:05 +02:00
pancake 1370dc15ce Improve the ci for binary r2pm builds 2026-09-30 11:00:15 +02:00
pancake d448e54eb8 Fix integer overflow bug in the decompiler 2026-09-30 10:58:54 +02:00
pancake 5faf591cb0 Add the ability to extract react native version string from libhermes.so 2026-09-28 18:22:41 +02:00
pancake a9e5b02a47 Release 1.5.0 2026-09-07 14:01:34 +02:00
pancake ce854d747f Fix build with latest r2 6.2.2 2026-09-07 13:57:11 +02:00
pancake dfc3a0f788 Use R2V 6.2.2 in the ci 2026-09-07 13:48:58 +02:00
pancake 0e2bdf4171 Add Hermes SwitchImm targets to analysis ##arch 2026-08-26 19:57:17 +02:00
pancake fbeb335107 Expose async, generator and operand metadata via rbin 2026-08-26 19:31:01 +02:00
pancake 1021280afe Fix UTF-16 strings in parser and rbin metadata 2026-08-26 19:06:17 +02:00
pancake 9089923bf8 Expose trycatchblocks via rbin iw interface 2026-08-26 18:28:48 +02:00
pancake 6ef311dbf7 Fix esil for comparison operations 2026-08-26 17:14:52 +02:00
pancake d89306435e Expose string byte lengths in opex 2026-08-26 16:07:06 +02:00
pancake 9eebb355ec Fix LoadParam ESIL frame slot modeling 2026-08-26 15:50:51 +02:00
pancake e80509d031 Fix test 2026-08-26 12:02:28 +02:00
pancake 16d01a2f15 Expose implicit Hermes call-frame arguments via opex 2026-08-26 11:59:48 +02:00
pancake 09752c456f Fix fake ABI roles in the HBC register profile 2026-08-26 11:42:50 +02:00
pancake 5d647b945f Add HBC function argument and return metadata 2026-08-26 11:26:17 +02:00
pancake 514ed8e7f8 Expose resolved strings and function operands via opex 2026-08-26 09:49:35 +02:00
pancake 4863d19818 Fix build for latest r2 (>=6.2.1) 2026-08-26 09:41:14 +02:00
60 changed files with 1708 additions and 275 deletions

No files matched your search

-3
View File
@@ -4,8 +4,5 @@ updates:
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "##build "
prefix-development: "##build "
labels:
- buildsystem
+66 -15
View File
@@ -1,5 +1,8 @@
name: CI
env:
R2V: 6.2.6
permissions:
contents: write
@@ -16,14 +19,16 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Install build deps
run: sudo apt-get update -y && sudo apt-get install -y build-essential autoconf automake libtool pkg-config
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build library
run: make
- name: Build r2 plugin
@@ -52,7 +57,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
@@ -61,13 +66,15 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build Debian package
run: make -C dist/debian SUDO=
- name: Upload Debian package artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: debian-package
path: dist/debian/*.deb
@@ -79,10 +86,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.12.x
@@ -93,7 +100,9 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build and install with Meson
shell: cmd
@@ -122,22 +131,56 @@ jobs:
run: |
Compress-Archive -Path install\* -DestinationPath r2hermes-windows-x64.zip -Force
- name: Package r2pm binary
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path bindist\plugins,bindist\bin | Out-Null
Copy-Item install\lib\radare2\plugins\*.dll bindist\plugins
Copy-Item install\bin\r2hermes.exe bindist\bin
Compress-Archive -Path bindist\* -DestinationPath "r2hermes-$env:R2V-windows-x86-64.zip" -Force
- name: Upload Windows package artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: windows-package
path: r2hermes-windows-x64.zip
path: r2hermes-*.zip
if-no-files-found: error
bindist:
name: Build r2pm binary (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install radare2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Package r2pm binary
run: make bindist
- name: Upload r2pm binary artifact
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: bindist-${{ matrix.os }}
path: r2hermes-*.zip
if-no-files-found: error
release:
name: Create release and upload artifacts
runs-on: ubuntu-latest
needs: [build, debian, windows]
needs: [build, debian, windows, bindist]
if: needs.build.outputs.tag != ''
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
@@ -149,25 +192,33 @@ jobs:
ls -l libhbc*
- name: Download Debian package artifact
uses: actions/download-artifact@v8
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: debian-package
path: release-artifacts
- name: Download Windows package artifact
uses: actions/download-artifact@v8
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: windows-package
path: release-artifacts/windows
- name: Download r2pm binary artifacts
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: bindist-*
merge-multiple: true
path: release-artifacts/bindist
- name: Create GitHub Release
uses: softprops/[email protected].1
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
tag_name: ${{ needs.build.outputs.tag }}
files: |
./libhbc-${{ needs.build.outputs.tag }}.tgz
./release-artifacts/*.deb
./release-artifacts/windows/*.zip
./release-artifacts/bindist/*.zip
make_latest: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+7 -2
View File
@@ -1,5 +1,8 @@
name: Meson Build
env:
R2V: 6.2.2
on:
push:
workflow_dispatch: {}
@@ -11,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
@@ -20,7 +23,9 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build library and r2 plugin with meson
run: meson setup builddir && meson compile -C builddir
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Checkout TinyCC repository
@@ -49,7 +49,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Install build dependencies
+9 -4
View File
@@ -1,5 +1,8 @@
name: Windows Build
env:
R2V: 6.2.2
on:
push:
workflow_dispatch: {}
@@ -11,10 +14,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.12.x
@@ -25,7 +28,9 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build and install with Meson
shell: cmd
@@ -55,7 +60,7 @@ jobs:
Compress-Archive -Path install\* -DestinationPath r2hermes-windows-x64.zip -Force
- name: Upload artifacts
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: windows-package
path: r2hermes-windows-x64.zip
+2
View File
@@ -1,3 +1,4 @@
.pmai/
build/
bin/
third_party/
@@ -8,3 +9,4 @@ third_party/
test/bins
*.swp
include/hbc/version.h
*.zip
+43 -1
View File
@@ -1,5 +1,9 @@
CC?= gcc
CFLAGS?=-Wall -Wextra -std=c11 -pedantic -O2 -fPIC -fvisibility=hidden -D_POSIX_C_SOURCE=200809L
ifeq ($(shell uname),Darwin)
# macOS hides memmem() (used by r_util headers) under strict _POSIX_C_SOURCE
CFLAGS+=-D_DARWIN_C_SOURCE
endif
DEBUG_FLAGS?=-g -DDEBUG -DHBC_DEBUG_LOGGING=1
VERSION=$(shell grep "^[[:space:]]*version:" meson.build | cut -d "'" -f 2)
@@ -99,6 +103,7 @@ test: $(BIN_FILE) $(TEST_BIN)
$(BIN_FILE) Ej test/bins/hbc/bespoke_eval.hbc | grep -q '"strict":false'
$(BIN_FILE) h test/bins/hbc/bespoke_eval.hbc | grep -q 'Source Locations: 13 bytes'
$(BIN_FILE) h test/bins/hbc/index.android.bundle | grep -q 'Source Locations: 0 bytes'
$(BIN_FILE) s test/bins/hbc/index.android.bundle 2959 | grep -q 'name=•'
$(BIN_FILE) libs test/bins/hbc/index.android.bundle | grep -q 'react-native'
r2:
@@ -113,6 +118,43 @@ user-install user-uninstall:
$(MAKE) -C src/r2
$(MAKE) -C src/r2 $@
# Binary package for r2pm -bi: plugins/ and bin/ for this r2 version and platform
# Binary package for r2pm -bi: plugins/ and bin/ for this r2 version and platform
# R2PM_OS/ARCH/BITS require a recent r2pm; fall back to uname when missing
R2PM_OS_SH=$(shell r2pm -H R2PM_OS 2>/dev/null)
R2PM_ARCH_SH=$(shell r2pm -H R2PM_ARCH 2>/dev/null)
R2PM_BITS_SH=$(shell r2pm -H R2PM_BITS 2>/dev/null)
ifeq ($(R2PM_OS_SH),)
ifeq ($(shell uname -s),Darwin)
R2PM_OS_SH=darwin
else
R2PM_OS_SH=linux
endif
endif
ifeq ($(R2PM_ARCH_SH),)
ifeq ($(shell uname -m),x86_64)
R2PM_ARCH_SH=x86
else ifeq ($(shell uname -m),aarch64)
R2PM_ARCH_SH=arm
else ifeq ($(shell uname -m),arm64)
R2PM_ARCH_SH=arm
else
R2PM_ARCH_SH=$(shell uname -m)
endif
endif
ifeq ($(R2PM_BITS_SH),)
R2PM_BITS_SH=64
endif
BINDIST_ZIP=r2hermes-$(shell r2 -qv)-$(R2PM_OS_SH)-$(R2PM_ARCH_SH)-$(R2PM_BITS_SH).zip
bindist: all
$(MAKE) -C src/r2
rm -rf $(BUILD_DIR)/bindist $(BINDIST_ZIP)
$(MAKE) -C src/r2 user-install R2_PLUGDIR=$(CURDIR)/$(BUILD_DIR)/bindist/plugins
mkdir -p $(BUILD_DIR)/bindist/bin
cp -f $(BIN_FILE) $(BUILD_DIR)/bindist/bin/
cd $(BUILD_DIR)/bindist && zip -r $(CURDIR)/$(BINDIST_ZIP) plugins bin
$(VH):
@mkdir -p $(dir $@)
echo '#ifndef LIBHBC_VERSION' > $@
@@ -122,4 +164,4 @@ $(VH):
echo '#define LIBHBC_VERSION_PATCH "$(shell echo $(VERSION) | cut -d . -f 3)"' >> $@
echo '#endif' >> $@
.PHONY: r2 test test2 all clean debug asan
.PHONY: r2 test test2 all clean debug asan bindist
+18
View File
@@ -81,12 +81,30 @@ Usage: pd:h[subcommand]
[0x00000000]> r2hermes-?
Usage: r2hermes[-arg] # see also pd:h for decompilation
r2hermes-h - help message (same as r2hermes-?, see pd:h? too)
r2hermes-A - Import symbols and entrypoints
r2hermes-AA - Also import strings and SLP literal comments/xrefs
r2hermes-AAA - Also analyze bytecode references
r2hermes-AAAA - Also perform full analysis (aaa)
r2hermes-E[jq] - List direct eval instruction sites (j=JSON, q=addresses only)
r2hermes-H - Show file information and hash status
r2hermes-L[?] - SLP literal cache: list/scan/reset/format/toggle
r2hermes-S[jr?] - emit SBOM from SLP literals (j=CycloneDX JSON, r=raw input)
```
The analysis levels are cumulative, like `r2flutter-A` and `r2unity-A`.
Use `r2hermes-A` to import symbols and entrypoint flags, and `r2hermes-AA` to
also import string flags and metadata, plus SLP literal flags, comments, and
constructor references. `r2hermes-AAA` adds reference analysis over each
function's bytecode range; `r2hermes-AAAA` additionally runs `aaa` to analyze
functions and control flow. Addresses follow `io.va`, and the commands preserve
the current seek and flagspace. Use `r2hermes-A?` for analysis help.
`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
+3
View File
@@ -7,6 +7,9 @@
#include <hbc/opcodes.h>
#include <hbc/parser.h>
/* Registers reserved by Hermes above an outgoing call's this/argument window. */
#define HBC_CALL_FRAME_RESERVED 6
/* Parsed instruction */
typedef struct {
const Instruction *inst;
+26
View File
@@ -57,14 +57,33 @@ typedef struct HBCStringMeta {
HBCStringKind kind;
} HBCStringMeta;
typedef enum {
HBC_FUNCTION_NONE = 0,
HBC_FUNCTION_ASYNC = 1u << 0,
HBC_FUNCTION_GENERATOR = 1u << 1
} HBCFunctionFlags;
/* Function metadata structure */
typedef struct {
const char *name; // Valid while provider is alive; caller must not free
u32 offset; // Bytecode offset in file
u32 size; // Size in bytes
u32 param_count; // Number of parameters
u32 flags; // HBCFunctionFlags
} HBCFunc;
/* Function-relative exception-handler range. */
typedef struct {
u32 start;
u32 end;
u32 target;
} HBCExceptionHandler;
typedef struct {
HBCExceptionHandler *handlers;
u32 count;
} HBCExceptionHandlerArray;
typedef struct {
u32 function_id;
u32 address; // File-absolute bytecode address
@@ -254,6 +273,13 @@ HBC_API Result hbc_get_header(HBC *hbc, HBCHeader *out);
*/
HBC_API Result hbc_get_function_info(HBC *hbc, u32 function_id, HBCFunc *out);
/** Get a copy of the exception handlers for one function. */
HBC_API Result hbc_get_function_exception_handlers(HBC *hbc, u32 function_id, HBCExceptionHandlerArray *out);
HBC_API void hbc_free_exception_handlers(HBCExceptionHandlerArray *arr);
/** Get the register-frame size for a function without exposing parser internals. */
HBC_API Result hbc_get_function_frame_size(HBC *hbc, u32 function_id, u32 *out);
/**
* Get a string by index.
*/
+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;
+1 -1
View File
@@ -1,7 +1,7 @@
project(
'r2hermes',
'c',
version: '1.4.8',
version: '1.6.2',
license: 'MIT',
default_options: [
'c_std=c11',
+2 -2
View File
@@ -100,7 +100,7 @@ static Result u32set_init(U32Set *s, u32 max_addr) {
if (!s) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "u32set init");
}
s->bitmap_size = (max_addr + 7) / 8;
s->bitmap_size = ((u64)max_addr + 7) / 8;
s->bitmap = (u8 *)calloc (s->bitmap_size, 1);
if (!s->bitmap) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "u32set bitmap");
@@ -2962,7 +2962,7 @@ Result _hbc_output_code(HermesDecompiler *state, DecompiledFunctionBody *functio
for (HbcTokenString *si_iter = R_VEC_START_ITER (&function_body->statements); si_iter != R_VEC_END_ITER (&function_body->statements); si_iter++) {
size_t si = (size_t) (si_iter - R_VEC_START_ITER (&function_body->statements));
/* Stop when budget exhausted or earlier phase (pass2) flagged it. */
if (state->output_truncated || (state->options.max_output_bytes > 0 && r_strbuf_length (out) >= state->options.max_output_bytes)) {
if (state->output_truncated || (state->options.max_output_bytes > 0 && r_strbuf_length (out) >= (size_t)state->options.max_output_bytes)) {
state->output_truncated = true;
if (!state->truncation_marker_emitted) {
state->truncation_marker_emitted = true;
-2
View File
@@ -234,8 +234,6 @@ static Result emit_call_fixed(HbcTokenString *out, const ParsedInstruction *insn
/* Hermes reserves this many frame-header registers at the very top of a
* function's register frame; an outgoing call's `this`+args sit just below. */
#define HBC_CALL_FRAME_RESERVED 6
/* Variable-arity Call/Construct. Hermes places the call's argument registers at
* the top of the caller's frame, below the reserved header: `this` at
* frame_size-1-RESERVED, the explicit arguments descending below it.
+144 -4
View File
@@ -15,6 +15,15 @@
#include <stdlib.h>
#include <stdio.h>
typedef struct {
u32 source;
u32 target;
} HBCFunctionEdge;
R_VEC_TYPE(RVecHBCFunctionEdge, HBCFunctionEdge)
static size_t decode_operands(const Instruction *inst, const u8 *bytes, size_t len, u32 values[6]);
/* ============================================================================
* HBC - Direct File Access API Implementation
* ============================================================================ */
@@ -127,6 +136,7 @@ void hbc_close(HBC *hbc) {
return;
}
hbc_literals_reset (hbc);
free (hbc->function_flags);
_hbc_reader_cleanup (&hbc->reader);
free (hbc);
}
@@ -153,6 +163,89 @@ Result hbc_get_header(HBC *hbc, HBCHeader *out) {
return SUCCESS_RESULT ();
}
static bool opcode_is(const Instruction *insn, const char *name, const char *long_name) {
const char *mnemonic = insn? insn->name: NULL;
return mnemonic && (!strcmp (mnemonic, name) || !strcmp (mnemonic, long_name));
}
static void add_function_edge(RVecHBCFunctionEdge *edges, u32 source, u32 target) {
HBCFunctionEdge edge = { .source = source, .target = target };
RVecHBCFunctionEdge_push_back (edges, &edge);
}
static void hbc_ensure_function_flags(HBC *hbc) {
if (!hbc || hbc->function_flags_ready) {
return;
}
hbc->function_flags_ready = true;
const u32 count = hbc->reader.header.functionCount;
if (!count) {
return;
}
hbc->function_flags = (u32 *)calloc (count, sizeof (u32));
if (!hbc->function_flags) {
return;
}
RVecHBCFunctionEdge edges;
RVecHBCFunctionEdge_init (&edges);
const HBCISA isa = hbc_isa_getv (hbc->reader.header.version);
for (u32 function_id = 0; function_id < count; function_id++) {
const u8 *code = NULL;
u32 size = 0;
if (hbc_get_function_bytecode (hbc, function_id, &code, &size).code != RESULT_SUCCESS || !code) {
continue;
}
for (u32 pc = 0; pc < size;) {
const u8 opcode = code[pc];
const Instruction *insn = opcode < isa.count? &isa.instructions[opcode]: NULL;
if (!insn || !insn->name || !insn->binary_size) {
break;
}
u32 values[6];
const size_t decoded_size = decode_operands (insn, code + pc, size - pc, values);
if (!decoded_size) {
break;
}
const u32 target = values[2];
if (target >= count) {
pc += (u32)decoded_size;
continue;
}
if (opcode_is (insn, "create_async_closure", "create_async_closure_long_index")) {
hbc->function_flags[target] |= HBC_FUNCTION_ASYNC;
} else if (opcode_is (insn, "create_generator_closure", "create_generator_closure_long_index")) {
hbc->function_flags[target] |= HBC_FUNCTION_GENERATOR;
add_function_edge (&edges, function_id, target);
} else if (opcode_is (insn, "create_generator", "create_generator_long_index")) {
hbc->function_flags[function_id] |= HBC_FUNCTION_GENERATOR;
hbc->function_flags[target] |= HBC_FUNCTION_GENERATOR;
add_function_edge (&edges, function_id, target);
}
pc += (u32)decoded_size;
}
}
bool changed;
do {
changed = false;
HBCFunctionEdge *edge;
R_VEC_FOREACH (&edges, edge) {
if ((hbc->function_flags[edge->source] & HBC_FUNCTION_ASYNC) &&
! (hbc->function_flags[edge->target] & HBC_FUNCTION_ASYNC)) {
hbc->function_flags[edge->target] |= HBC_FUNCTION_ASYNC;
changed = true;
}
}
} while (changed);
for (u32 i = 0; i < count; i++) {
if (hbc->function_flags[i] & HBC_FUNCTION_ASYNC) {
hbc->function_flags[i] &= ~HBC_FUNCTION_GENERATOR;
}
}
RVecHBCFunctionEdge_fini (&edges);
}
Result hbc_get_function_info(HBC *hbc, u32 function_id, HBCFunc *out) {
if (!hbc || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
@@ -173,6 +266,57 @@ Result hbc_get_function_info(HBC *hbc, u32 function_id, HBCFunc *out) {
out->offset = fh->offset;
out->size = fh->bytecodeSizeInBytes;
out->param_count = fh->paramCount;
hbc_ensure_function_flags (hbc);
out->flags = hbc->function_flags? hbc->function_flags[function_id]: HBC_FUNCTION_NONE;
return SUCCESS_RESULT ();
}
Result hbc_get_function_exception_handlers(HBC *hbc, u32 function_id, HBCExceptionHandlerArray *out) {
if (!hbc || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
}
out->handlers = NULL;
out->count = 0;
HBCReader *reader = &hbc->reader;
if (!reader->function_headers || function_id >= reader->header.functionCount) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Function ID out of range");
}
if (!reader->function_id_to_exc_handlers) {
return SUCCESS_RESULT ();
}
const ExceptionHandlerList *source = &reader->function_id_to_exc_handlers[function_id];
if (!source->count) {
return SUCCESS_RESULT ();
}
if (!source->handlers) {
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "Invalid exception handler table");
}
HBCExceptionHandler *handlers = (HBCExceptionHandler *)calloc (source->count, sizeof (HBCExceptionHandler));
if (!handlers) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Cannot allocate exception handlers");
}
for (u32 i = 0; i < source->count; i++) {
handlers[i].start = source->handlers[i].start;
handlers[i].end = source->handlers[i].end;
handlers[i].target = source->handlers[i].target;
}
out->handlers = handlers;
out->count = source->count;
return SUCCESS_RESULT ();
}
void hbc_free_exception_handlers(HBCExceptionHandlerArray *arr) {
if (arr) {
R_FREE (arr->handlers);
arr->count = 0;
}
}
Result hbc_get_function_frame_size(HBC *hbc, u32 function_id, u32 *out) {
if (!hbc || !out || !hbc->reader.function_headers || function_id >= hbc->reader.header.functionCount) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
}
*out = hbc->reader.function_headers[function_id].frameSize;
return SUCCESS_RESULT ();
}
@@ -721,10 +865,6 @@ static char *build_literal_comment(HBCReader *reader, const char *mnemonic, cons
return result;
}
/* Read each operand by type into values[6], little-endian with sign-extension
* for relative addresses; returns total instruction size, or 0 if truncated. */
static size_t decode_operands(const Instruction *inst, const u8 *bytes, size_t len, u32 values[6]);
Result hbc_dec(const HBCDecodeCtx *ctx, HBCInsnInfo *out) {
if (!ctx || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid context");
+2
View File
@@ -31,6 +31,8 @@ struct HBC {
u32 string_count;
const char **strings;
u32 function_count;
u32 *function_flags;
bool function_flags_ready;
HBCLiteralCache lit_cache;
};
+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);
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v51[167] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v58[176] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v59[177] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v61[177] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v62[177] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v68[177] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v69[177] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v70[177] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v72[177] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+2 -2
View File
@@ -87,8 +87,8 @@ static const Instruction k_instructions_v73[180] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+9 -9
View File
@@ -81,14 +81,14 @@ static const Instruction k_instructions_v76[256] = {
[74] = { "call", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[75] = { "construct", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[76] = { "call1", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[77] = { "call_direct", { { R8, 0 }, { U8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[77] = { "call_direct", { { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[78] = { "call2", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[79] = { "call3", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 } }, 6 },
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
@@ -98,10 +98,10 @@ static const Instruction k_instructions_v76[256] = {
[91] = { "async_break_check", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[92] = { "profile_point", { { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[93] = { "unreachable", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[94] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[95] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[96] = { "create_generator_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[97] = { "create_generator_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[94] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[95] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[96] = { "create_generator_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[97] = { "create_generator_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[98] = { "create_this", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[99] = { "select_object", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[100] = { "load_param", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
@@ -129,8 +129,8 @@ static const Instruction k_instructions_v76[256] = {
[122] = { "start_generator", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[123] = { "resume_generator", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[124] = { "complete_generator", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[125] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[126] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[125] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[126] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[127] = { "iterator_begin", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[128] = { "iterator_next", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[129] = { "iterator_close", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -87,9 +87,9 @@ static const Instruction k_instructions_v80[181] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[88] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -87,9 +87,9 @@ static const Instruction k_instructions_v81[183] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[88] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -87,10 +87,10 @@ static const Instruction k_instructions_v82[185] = {
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[87] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[88] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[89] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -88,10 +88,10 @@ static const Instruction k_instructions_v83[185] = {
[81] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[82] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[86] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[84] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[86] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[89] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[90] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+13 -13
View File
@@ -82,16 +82,16 @@ static const Instruction k_instructions_v84[256] = {
[75] = { "call", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[76] = { "construct", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[77] = { "call1", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[78] = { "call_direct", { { R8, 0 }, { U8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[78] = { "call_direct", { { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[79] = { "call2", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[80] = { "call3", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 } }, 6 },
[81] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[82] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[86] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[84] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[86] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[89] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[90] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
@@ -100,12 +100,12 @@ static const Instruction k_instructions_v84[256] = {
[93] = { "debugger", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[94] = { "async_break_check", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[95] = { "profile_point", { { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[96] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[97] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[98] = { "create_generator_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[99] = { "create_generator_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[100] = { "create_async_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[101] = { "create_async_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[96] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[97] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[98] = { "create_generator_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[99] = { "create_generator_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[100] = { "create_async_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[101] = { "create_async_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[102] = { "create_this", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[103] = { "select_object", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[104] = { "load_param", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
@@ -134,8 +134,8 @@ static const Instruction k_instructions_v84[256] = {
[127] = { "start_generator", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[128] = { "resume_generator", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[129] = { "complete_generator", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[130] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[131] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[130] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[131] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[132] = { "iterator_begin", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[133] = { "iterator_next", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[134] = { "iterator_close", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -90,10 +90,10 @@ static const Instruction k_instructions_v85[201] = {
[83] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[84] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -90,10 +90,10 @@ static const Instruction k_instructions_v86[201] = {
[83] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[84] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -90,10 +90,10 @@ static const Instruction k_instructions_v87[204] = {
[83] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[84] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -90,10 +90,10 @@ static const Instruction k_instructions_v89[256] = {
[83] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[84] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -90,10 +90,10 @@ static const Instruction k_instructions_v90[256] = {
[83] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[84] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -92,10 +92,10 @@ static const Instruction k_instructions_v92[206] = {
[85] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[86] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[92] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[93] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[94] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -90,10 +90,10 @@ static const Instruction k_instructions_v93[256] = {
[83] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[84] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -92,10 +92,10 @@ static const Instruction k_instructions_v94[256] = {
[85] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[86] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[92] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[93] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[94] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -92,10 +92,10 @@ static const Instruction k_instructions_v95[256] = {
[85] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[86] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[92] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[93] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[94] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
+4 -4
View File
@@ -92,10 +92,10 @@ static const Instruction k_instructions_v96[256] = {
[85] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[86] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[92] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[93] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[94] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
+3 -3
View File
@@ -105,9 +105,9 @@ static const Instruction k_instructions_v97[200] = {
[98] = { "call3", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 } }, 6 },
[99] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[100] = { "call_with_new_target_long", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[101] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[102] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[103] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[101] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[102] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[103] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[104] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[105] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[106] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
+7 -7
View File
@@ -120,9 +120,9 @@ static const Instruction k_instructions_v98[219] = {
[112] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[113] = { "call_with_new_target_long", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[114] = { "call_require", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[115] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[116] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[117] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[115] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[116] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[117] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[118] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[119] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[120] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
@@ -220,8 +220,8 @@ static const Instruction k_instructions_v98[219] = {
[212] = { "j_strict_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[213] = { "j_strict_not_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[214] = { "j_strict_not_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[215] = { "jmp_builtin_is", { { A8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[216] = { "jmp_builtin_is_long", { { A32, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[217] = { "jmp_builtin_is_not", { { A8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[218] = { "jmp_builtin_is_not_long", { { A32, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[215] = { "jmp_builtin_is", { { A8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[216] = { "jmp_builtin_is_long", { { A32, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[217] = { "jmp_builtin_is_not", { { A8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[218] = { "jmp_builtin_is_not_long", { { A32, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
};
+7 -7
View File
@@ -119,9 +119,9 @@ static const Instruction k_instructions_v99[223] = {
[112] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[113] = { "call_with_new_target_long", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[114] = { "call_require", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[115] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[116] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[117] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[115] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[116] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[117] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[118] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[119] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[120] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
@@ -223,8 +223,8 @@ static const Instruction k_instructions_v99[223] = {
[216] = { "j_strict_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[217] = { "j_strict_not_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[218] = { "j_strict_not_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[219] = { "jmp_builtin_is", { { A8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[220] = { "jmp_builtin_is_long", { { A32, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[221] = { "jmp_builtin_is_not", { { A8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[222] = { "jmp_builtin_is_not_long", { { A32, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[219] = { "jmp_builtin_is", { { A8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[220] = { "jmp_builtin_is_long", { { A32, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[221] = { "jmp_builtin_is_not", { { A8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[222] = { "jmp_builtin_is_not_long", { { A32, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
};
+21 -11
View File
@@ -1,6 +1,8 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <hbc/parser.h>
#include <r_lib.h>
#include <r_util/r_str.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -884,7 +886,12 @@ Result _hbc_reader_read_string_tables(HBCReader *reader) {
continue;
}
char *str = (char *)malloc (length * bpc + 1);
size_t decoded_size = 0;
RETURN_IF_ERROR (checked_count_size (length, is_utf16? 3: 1, &decoded_size, "Decoded string is too large"));
if (decoded_size == SIZE_MAX) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Decoded string is too large");
}
char *str = (char *)malloc (decoded_size + 1);
if (!str) {
free (string_storage);
reader->string_table_storage = NULL;
@@ -893,17 +900,20 @@ Result _hbc_reader_read_string_tables(HBCReader *reader) {
/* Copy string data */
if (is_utf16) {
/* UTF-16 string */
/* Note: This is a simplified version that doesn't handle surrogate pairs */
for (u32 j = 0; j < length; j++) {
u16 c = (string_storage[offset + j * 2] | (string_storage[offset + j * 2 + 1] << 8));
if (c < 128) {
str[j] = (char)c;
} else {
str[j] = '?'; /* Replace non-ASCII with? */
}
/* The final flag changed from little-endian to big-endian in ABI 115. */
#if R2_ABIVERSION >= 115
int written = r_str_utf16_to_utf8 ((ut8 *)str, decoded_size + 1,
string_storage + offset, length * 2, false);
#else
int written = r_str_utf16_to_utf8 ((ut8 *)str, decoded_size + 1,
string_storage + offset, length * 2, true);
#endif
if (written < 0) {
free (str);
free (string_storage);
reader->string_table_storage = NULL;
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Invalid UTF-16 string");
}
str[length] = '\0';
} else {
/* ASCII string */
memcpy (str, string_storage + offset, length);
+2 -1
View File
@@ -17,6 +17,7 @@ LDFLAGS += $(shell pkg-config --libs r_core 2>/dev/null || echo "$(if $(R2PM_LIN
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
EXT_SO = dylib
CFLAGS += -D_DARWIN_C_SOURCE
LDFLAGS += -dynamiclib -undefined dynamic_lookup
else ifeq ($(UNAME_S),Linux)
EXT_SO = so
@@ -35,7 +36,7 @@ BIN_SRC = bin_hbc.c
ASM_SRC = asm_hbc.c
CORE_SRC = core_hbc.c
CORE_ONE_SRC = core_hbc_one.c
HBC_HEADERS = $(shell find ../../include -name '*.h') utils.inc.c sbom.inc.c
HBC_HEADERS = $(shell find ../../include -name '*.h') utils.inc.c sbom.inc.c version.inc.c
# Object files
ARCH_OBJ = arch_hbc.o
+318 -39
View File
@@ -13,9 +13,17 @@
#define R_ARCH_SYNTAX_CAMEL 6
#endif
typedef struct {
u32 offset;
u32 size;
u32 frame_size;
} HermesFunctionFrame;
typedef struct {
ut32 bytecode_version; /* cached effective HBC bytecode version */
HBC *hbc; /* Hermes state for direct access */
HermesFunctionFrame *function_frames;
u32 function_frame_count;
u32 string_count;
u32 overflow_string_count;
const void *small_string_table;
@@ -23,6 +31,12 @@ typedef struct {
u64 string_storage_offset;
} HermesArchSession;
static int function_frame_cmp(const void *a, const void *b) {
const HermesFunctionFrame *fa = (const HermesFunctionFrame *)a;
const HermesFunctionFrame *fb = (const HermesFunctionFrame *)b;
return fa->offset < fb->offset? -1: fa->offset > fb->offset;
}
static ut32 parse_cpu_version(const char *cpu) {
if (!cpu || !*cpu) {
return 0;
@@ -87,6 +101,24 @@ static bool load_string_tables(HermesArchSession *hs, RArchSession *s) {
}
hs->bytecode_version = hh.version;
const u32 function_count = hbc_function_count (hs->hbc);
hs->function_frames = R_NEWS0 (HermesFunctionFrame, function_count);
if (function_count && !hs->function_frames) {
return false;
}
for (u32 i = 0; i < function_count; i++) {
HBCFunc fi = { 0 };
u32 frame_size = 0;
if (hbc_get_function_info (hs->hbc, i, &fi).code == RESULT_SUCCESS && fi.size &&
hbc_get_function_frame_size (hs->hbc, i, &frame_size).code == RESULT_SUCCESS) {
HermesFunctionFrame *frame = &hs->function_frames[hs->function_frame_count++];
frame->offset = fi.offset;
frame->size = fi.size;
frame->frame_size = frame_size;
}
}
qsort (hs->function_frames, hs->function_frame_count, sizeof (HermesFunctionFrame), function_frame_cmp);
/* Get string count */
hs->string_count = hbc_string_count (hs->hbc);
@@ -127,7 +159,94 @@ static bool opcode_is_conditional(u8 opcode) {
return false;
}
static void parse_operands_and_set_ptr(RAnalOp *op, const ut8 *bytes, ut32 size, ut8 opcode, HermesArchSession *hs) {
/* Return the register-frame size for the function containing addr. The cached
* intervals are sorted by bytecode offset, so this stays logarithmic even for
* large React Native bundles with thousands of functions. */
static bool function_frame_size(HermesArchSession *hs, ut64 addr, ut32 *frame_size) {
if (!hs || !hs->function_frames || !frame_size) {
return false;
}
const ut64 file_addr = addr >= HBC_VADDR_BASE? addr - HBC_VADDR_BASE: addr;
u32 lo = 0;
u32 hi = hs->function_frame_count;
while (lo < hi) {
const u32 mid = lo + (hi - lo) / 2;
if ((ut64)hs->function_frames[mid].offset <= file_addr) {
lo = mid + 1;
} else {
hi = mid;
}
}
if (!lo) {
return false;
}
/* Multiple deduplicated function-table entries may share the body. */
const u32 candidate_offset = hs->function_frames[lo - 1].offset;
u32 candidate_frame_size = 0;
for (u32 i = lo; i > 0; i--) {
const HermesFunctionFrame *frame = &hs->function_frames[i - 1];
if (frame->offset != candidate_offset) {
break;
}
if (file_addr < (ut64)frame->offset + frame->size) {
candidate_frame_size = R_MAX (candidate_frame_size, frame->frame_size);
}
}
*frame_size = candidate_frame_size;
return candidate_frame_size > 0;
}
static bool implicit_call_arg_count(ut8 opcode, const ut32 values[6], ut32 *argc) {
switch (opcode) {
case OP_Call:
case OP_CallLong:
case OP_Construct:
case OP_ConstructLong:
*argc = values[2];
return true;
case OP_CallDirect:
case OP_CallDirectLongIndex:
*argc = values[1];
return true;
case OP_CallBuiltin:
case OP_CallBuiltinLong:
*argc = values[2];
return true;
default:
return false;
}
}
/* Append the implicit call-frame window after the encoded operands. Hermes
* counts `this` in argc and stores it plus source arguments immediately below
* the six reserved frame-header registers. */
static void append_call_args(PJ *pj, HermesArchSession *hs, ut64 addr, ut8 opcode, const ut32 values[6]) {
ut32 argc = 0;
ut32 frame_size = 0;
if (!pj || !implicit_call_arg_count (opcode, values, &argc) || !argc ||
!function_frame_size (hs, addr, &frame_size)) {
return;
}
const st64 base = (st64)frame_size - 1 - HBC_CALL_FRAME_RESERVED;
if (base < 0 || argc > (ut32) (base + 1)) {
return;
}
for (ut32 i = 0; i < argc; i++) {
char reg[16];
snprintf (reg, sizeof (reg), "r%" PFMT64d, base - i);
pj_o (pj);
pj_ks (pj, "type", "reg");
pj_ks (pj, "value", reg);
pj_kb (pj, "implicit", true);
pj_ks (pj, "kind", i? "call_arg": "call_this");
if (i) {
pj_kn (pj, "index", i - 1);
}
pj_end (pj);
}
}
static void parse_operands(RAnalOp *op, const ut8 *bytes, ut32 size, ut8 opcode, ut8 canonical_opcode, HermesArchSession *hs, RArchDecodeMask mask) {
HBCISA isa = hbc_isa_getv (hs->bytecode_version);
const Instruction *inst_set = isa.instructions;
if (!inst_set || opcode >= isa.count) {
@@ -135,91 +254,211 @@ static void parse_operands_and_set_ptr(RAnalOp *op, const ut8 *bytes, ut32 size,
}
const Instruction *inst = &inst_set[opcode];
PJ *pj = NULL;
if (mask & R_ARCH_OP_MASK_OPEX) {
pj = pj_new ();
if (pj) {
pj_o (pj);
pj_ka (pj, "operands");
}
}
// Parse operands
ut32 operand_values[6] = { 0 };
size_t pos = 1; // Skip opcode byte
size_t pos = 1; /* Skip opcode byte */
ut32 values[6] = { 0 };
for (int i = 0; i < 6 && inst->operands[i].operand_type != OPERAND_TYPE_NONE; i++) {
if (pos >= size) {
break;
}
const InstructionOperand *operand = &inst->operands[i];
ut32 value = 0;
double double_value = 0.0;
bool valid = false;
switch (inst->operands[i].operand_type) {
case OPERAND_TYPE_REG8:
case OPERAND_TYPE_UINT8:
case OPERAND_TYPE_ADDR8:
if (pos < size) {
operand_values[i] = bytes[pos];
if (pos + 1 <= size) {
value = bytes[pos];
pos += 1;
valid = true;
}
break;
case OPERAND_TYPE_UINT16:
if (pos + 1 < size) {
operand_values[i] = (bytes[pos + 1] << 8) | bytes[pos];
if (pos + 2 <= size) {
value = r_read_le16 (bytes + pos);
pos += 2;
valid = true;
}
break;
case OPERAND_TYPE_REG32:
case OPERAND_TYPE_UINT32:
case OPERAND_TYPE_ADDR32:
case OPERAND_TYPE_IMM32:
if (pos + 3 < size) {
operand_values[i] = (bytes[pos + 3] << 24) | (bytes[pos + 2] << 16) |
(bytes[pos + 1] << 8) | bytes[pos];
if (pos + 4 <= size) {
value = r_read_le32 (bytes + pos);
pos += 4;
valid = true;
}
break;
case OPERAND_TYPE_DOUBLE:
/* Double is 8 bytes - just advance position, value not used for ptr setting */
if (pos + 7 < size) {
if (pos + 8 <= size) {
ut64 bits = r_read_le64 (bytes + pos);
memcpy (&double_value, &bits, sizeof (double_value));
pos += 8;
valid = true;
}
break;
case OPERAND_TYPE_NONE:
default:
/* OPERAND_TYPE_NONE is never reached due to loop condition */
break;
}
if (!valid) {
break;
}
values[i] = value;
// Check if this operand is a string ID
if (inst->operands[i].operand_meaning == OPERAND_MEANING_STRING_ID) {
ut32 string_id = operand_values[i];
ut64 ref_addr = UT64_MAX;
ut64 string_byte_length = UT64_MAX;
/* Resolve IDs without replacing their raw values in OPEX. */
if (operand->operand_meaning == OPERAND_MEANING_STRING_ID) {
ut32 string_id = value;
if (string_id < hs->string_count && hs->hbc) {
HBCStringMeta meta;
Result meta_result = hbc_get_string_meta (hs->hbc, string_id, &meta);
if (meta_result.code == RESULT_SUCCESS) {
op->ptr = (st64) (HBC_VADDR_BASE + meta.offset);
ref_addr = HBC_VADDR_BASE + meta.offset;
op->ptr = (st64)ref_addr;
string_byte_length = meta.isUTF16? (ut64)meta.length * 2: meta.length;
/* Expose the string byte length (size of the pointed
* contents) so the disassembler caps r2's string read and
* picks the right flag among overlapping, non-null-terminated
* Hermes strings that share this storage offset. */
op->ptrsize = meta.isUTF16? (int) (meta.length * 2): (int)meta.length;
op->ptrsize = (int)string_byte_length;
}
}
}
// Check if this operand is a function ID
else if (inst->operands[i].operand_meaning == OPERAND_MEANING_FUNCTION_ID) {
ut32 function_id = operand_values[i];
} else if (operand->operand_meaning == OPERAND_MEANING_FUNCTION_ID) {
ut32 function_id = value;
if (hs->hbc) {
ut32 offset = 0;
HBCFunc fi;
Result func_result = hbc_get_function_info (hs->hbc, function_id, &fi);
if (func_result.code == RESULT_SUCCESS) {
// name = fi.name;
offset = fi.offset;
(void)fi.size;
(void)fi.param_count;
op->ptr = (st64) (HBC_VADDR_BASE + offset);
ref_addr = HBC_VADDR_BASE + fi.offset;
op->ptr = (st64)ref_addr;
}
}
}
if (pj) {
pj_o (pj);
if (operand->operand_type == OPERAND_TYPE_REG8 || operand->operand_type == OPERAND_TYPE_REG32) {
char reg[16];
snprintf (reg, sizeof (reg), "r%u", value);
pj_ks (pj, "type", "reg");
pj_ks (pj, "value", reg);
} else {
pj_ks (pj, "type", "imm");
if (operand->operand_type == OPERAND_TYPE_DOUBLE) {
pj_kd (pj, "value", double_value);
} else if (operand->operand_meaning != OPERAND_MEANING_NONE) {
pj_kn (pj, "value", value);
} else if (operand->operand_type == OPERAND_TYPE_ADDR8) {
pj_kN (pj, "value", (st8)value);
} else if (operand->operand_type == OPERAND_TYPE_ADDR32 || operand->operand_type == OPERAND_TYPE_IMM32) {
pj_kN (pj, "value", (st32)value);
} else {
pj_kn (pj, "value", value);
}
}
if (operand->operand_meaning != OPERAND_MEANING_NONE) {
pj_ks (pj, "kind", hbc_operand_name (operand));
}
if (ref_addr != UT64_MAX) {
pj_kn (pj, "address", ref_addr);
}
if (string_byte_length != UT64_MAX) {
/* Unlike RAnalOp.ptrsize, OPEX can represent a known empty
* string without conflating its zero length with "unset". */
pj_kn (pj, "byte_length", string_byte_length);
}
pj_end (pj);
}
}
if (pj) {
append_call_args (pj, hs, op->addr, canonical_opcode, values);
pj_end (pj); /* operands */
pj_end (pj);
r_strbuf_set (&op->opex, pj_string (pj));
pj_free (pj);
}
}
/* SwitchImm stores instruction-relative deltas in a 4-byte aligned table
* outside the instruction stream. Publish the decoded cases through the
* standard switch_op interface so r2 analysis and external decompilers receive
* the same control-flow facts as libhbc's built-in decompiler. */
static bool decode_switch_op(RArchSession *s, RAnalOp *op) {
if (!s || !s->arch || !op || !op->bytes || op->size < 18) {
return false;
}
const ut32 table_delta = r_read_le32 (op->bytes + 2);
const st32 default_delta = (st32)r_read_le32 (op->bytes + 6);
const st32 min_val = (st32)r_read_le32 (op->bytes + 10);
const st32 max_val = (st32)r_read_le32 (op->bytes + 14);
const st64 amount64 = (st64)max_val - min_val + 1;
if (amount64 <= 0 || amount64 > UT16_MAX) {
return false;
}
const ut32 amount = (ut32)amount64;
const ut64 unaligned_table = op->addr + table_delta;
if (unaligned_table < op->addr) {
return false;
}
const ut64 table_addr = R_ROUND (unaligned_table, 4);
const ut64 default_target = op->addr + default_delta;
RBin *bin = s->arch->binb.bin;
if (!bin || !bin->iob.read_at) {
return false;
}
const int table_size = (int)amount * 4;
ut8 *table = malloc (table_size);
if (!table) {
return false;
}
if (!bin->iob.read_at (bin->iob.io, table_addr, table, table_size)) {
free (table);
return false;
}
op->switch_op = r_anal_switch_op_new (op->addr, (ut64) (st64)min_val, (ut64) (st64)max_val, default_target);
if (!op->switch_op) {
free (table);
return false;
}
#if R2_ABIVERSION >= 33
op->switch_op->daddr = table_addr;
op->switch_op->dsize = 4;
op->switch_op->amount = amount;
#endif
for (ut32 i = 0; i < amount; i++) {
const st32 delta = (st32)r_read_le32 (table + i * 4);
const ut64 target = op->addr + delta;
if (target != default_target) {
r_anal_switch_op_add_case (op->switch_op, table_addr + i * 4, (ut64) (st64)min_val + i, target);
}
}
free (table);
op->fail = default_target;
op->eob = true;
return true;
}
static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
R_RETURN_VAL_IF_FAIL (s && op, false);
(void)mask;
if (!op->bytes) {
return false;
@@ -283,8 +522,8 @@ static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
op->type = R_ANAL_OP_TYPE_UNK;
op->family = R_ANAL_OP_FAMILY_CPU;
/* Parse operands and set ptr for string/function references */
parse_operands_and_set_ptr (op, op->bytes, op->size, sinfo.opcode, hs);
/* Parse structured operands and set ptr for string/function references. */
parse_operands (op, op->bytes, op->size, sinfo.opcode, sinfo.canonical_opcode, hs, mask);
if (mask & R_ARCH_OP_MASK_ESIL && sinfo.text) {
set_esil (op, op->bytes, op->addr);
@@ -510,6 +749,7 @@ static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
/* Switch operations */
case OP_SwitchImm:
op->type = R_ANAL_OP_TYPE_SWITCH;
decode_switch_op (s, op);
break;
/* Special operations */
case OP_Unreachable:
@@ -642,6 +882,8 @@ static bool fini(RArchSession *s) {
}
HermesArchSession *hs = (HermesArchSession *)s->data;
if (hs) {
R_FREE (hs->function_frames);
hs->function_frame_count = 0;
if (hs->hbc) {
hbc_close (hs->hbc);
hs->hbc = NULL;
@@ -652,6 +894,48 @@ static bool fini(RArchSession *s) {
return true;
}
/* LoadParam reads a Hermes call-frame slot, not an architectural register.
* Keep the slot index as an ESIL operand and use a custom value-producing op
* so register-access analysis never invents arg0/arg1/... register names.
* Concrete emulation cannot recover the caller-owned frame value here; zero is
* the conventional unknown placeholder, while OPEX retains the exact slot. */
static bool esil_hbc_load_param(REsil *esil) {
R_RETURN_VAL_IF_FAIL (esil, false);
#if R2_ABIVERSION >= 87
const RStrs slot = r_esil_pop (esil);
if (r_strs_empty (slot)) {
return false;
}
#else
char *slot = r_esil_pop (esil);
if (!slot) {
return false;
}
free (slot);
#endif
return r_esil_pushnum (esil, 0);
}
#if R2_ABIVERSION >= 109
static bool esilcb(RArchSession *s R_UNUSED, REsil *esil, RArchEsilAction action) {
#else
static bool esilcb(RArchSession *s, RArchEsilAction action) {
REsil *esil = s && s->arch? s->arch->esil: NULL;
#endif
if (!esil) {
return false;
}
switch (action) {
case R_ARCH_ESIL_ACTION_INIT:
return r_esil_set_op (esil, "HBC_LOAD_PARAM", esil_hbc_load_param,
1, 1, R_ESIL_OP_TYPE_CUSTOM, NULL);
case R_ARCH_ESIL_ACTION_FINI:
return true;
default:
return false;
}
}
/* Register profile for ESIL emulation */
static char *regs(RArchSession *s) {
(void)s;
@@ -660,14 +944,8 @@ static char *regs(RArchSession *s) {
return NULL;
}
/* Define special registers */
/* Hermes frame arguments and VM registers have no fixed ABI homes. */
r_strbuf_append (sb, "=PC\tpc\n");
r_strbuf_append (sb, "=SP\tr0\n");
r_strbuf_append (sb, "=BP\tr1\n");
r_strbuf_append (sb, "=A0\tr2\n");
r_strbuf_append (sb, "=A1\tr3\n");
r_strbuf_append (sb, "=A2\tr4\n");
r_strbuf_append (sb, "=A3\tr5\n");
/* Program counter - 64 bits at offset 0 */
r_strbuf_append (sb, "gpr\tpc\t.64\t0\t0\n");
@@ -697,6 +975,7 @@ const RArchPlugin r_arch_plugin_r2hermes = {
.regs = regs,
.info = archinfo,
.mnemonics = mnemonics,
.esilcb = esilcb,
.init = init,
.fini = fini,
};
+126 -3
View File
@@ -11,6 +11,10 @@
typedef struct {
HBC *hbc;
#if R2_ABIVERSION >= 142
RVecRBinTrycatch trycatch;
bool trycatch_loaded;
#endif
} HBCBinObj;
static bool check(RBinFile *bf, RBuffer *b) {
@@ -29,6 +33,9 @@ static bool load(RBinFile *bf, RBuffer *buf, ut64 R_UNUSED loadaddr) {
if (hbc_open_from_buffer (buf, &hbc).code == RESULT_SUCCESS) {
HBCBinObj *bo = R_NEW0 (HBCBinObj);
bo->hbc = hbc;
#if R2_ABIVERSION >= 142
RVecRBinTrycatch_init (&bo->trycatch);
#endif
bf->bo->bin_obj = bo;
bf->buf = buf;
return true;
@@ -40,6 +47,9 @@ static bool load(RBinFile *bf, RBuffer *buf, ut64 R_UNUSED loadaddr) {
static void destroy(RBinFile *bf) {
HBCBinObj *bo = bf->bo->bin_obj;
if (bo) {
#if R2_ABIVERSION >= 142
RVecRBinTrycatch_fini (&bo->trycatch);
#endif
hbc_safe_close (&bo->hbc);
free (bo);
}
@@ -239,10 +249,110 @@ static RList *entries(RBinFile *bf) {
return entries;
}
#if R2_ABIVERSION >= 142
static RVecRBinTrycatch *trycatch(RBinFile *bf) {
HBCBinObj *bo = R_UNWRAP3 (bf, bo, bin_obj);
if (!bo || bo->trycatch_loaded) {
return bo? &bo->trycatch: NULL;
}
HBC *hbc = bo->hbc;
if (!hbc) {
return NULL;
}
const u32 function_count = hbc_function_count (hbc);
for (u32 function_id = 0; function_id < function_count; function_id++) {
HBCFunc function;
if (hbc_get_function_info (hbc, function_id, &function).code != RESULT_SUCCESS) {
continue;
}
HBCExceptionHandlerArray handlers = { 0 };
if (hbc_get_function_exception_handlers (hbc, function_id, &handlers).code != RESULT_SUCCESS) {
continue;
}
const ut64 source = HBC_VADDR_BASE + function.offset;
for (u32 i = 0; i < handlers.count; i++) {
const HBCExceptionHandler *handler = &handlers.handlers[i];
if (handler->start >= handler->end || handler->end > function.size || handler->target >= function.size) {
continue;
}
(void)r_bin_trycatch_add (&bo->trycatch,
source,
source + handler->start,
source + handler->end,
source + handler->target,
0);
}
hbc_free_exception_handlers (&handlers);
}
bo->trycatch_loaded = true;
return &bo->trycatch;
}
#elif R2_ABIVERSION >= 14
static RList *trycatch(RBinFile *bf) {
RList *ret = r_list_newf ((RListFree)r_bin_trycatch_free);
HBC *hbc = get_hbc (bf);
if (!ret || !hbc) {
return ret;
}
const u32 function_count = hbc_function_count (hbc);
for (u32 function_id = 0; function_id < function_count; function_id++) {
HBCFunc function;
if (hbc_get_function_info (hbc, function_id, &function).code != RESULT_SUCCESS) {
continue;
}
HBCExceptionHandlerArray handlers = { 0 };
if (hbc_get_function_exception_handlers (hbc, function_id, &handlers).code != RESULT_SUCCESS) {
continue;
}
const ut64 source = HBC_VADDR_BASE + function.offset;
for (u32 i = 0; i < handlers.count; i++) {
const HBCExceptionHandler *handler = &handlers.handlers[i];
if (handler->start >= handler->end || handler->end > function.size || handler->target >= function.size) {
continue;
}
RBinTrycatch *tc = r_bin_trycatch_new (source,
source + handler->start,
source + handler->end,
source + handler->target,
0);
if (tc) {
r_list_append (ret, tc);
}
}
hbc_free_exception_handlers (&handlers);
}
return ret;
}
#endif
static ut64 baddr(RBinFile *bf R_UNUSED) {
return HBC_VADDR_BASE;
}
static void set_symbol_size(RBinSymbol *symbol, ut32 size) {
#if R2_ABIVERSION >= 134
symbol->attr.size = size;
#else
symbol->size = size;
#endif
}
static void set_function_signature(RBinSymbol *symbol, const HBCFunc *func) {
#if R2_ABIVERSION >= 100
/* LoadParam indexes the Hermes frame arguments; they are not registers. */
if (func->param_count <= UT16_MAX) {
symbol->cc_arg_count = func->param_count;
}
symbol->ret_count = 1;
#else
(void)symbol;
(void)func;
#endif
}
static void append_binding_symbols(RList *symbols, HBC *hbc) {
HBCBindings bindings = { 0 };
if (hbc_scan_bindings (hbc, &bindings).code != RESULT_SUCCESS) {
@@ -259,7 +369,7 @@ static void append_binding_symbols(RList *symbols, HBC *hbc) {
r_bin_name_filtered (sym->name, nm);
sym->paddr = b->offset;
sym->vaddr = HBC_VADDR_BASE + b->offset;
sym->size = 1;
set_symbol_size (sym, 1);
sym->ordinal = b->string_id;
sym->type = R_BIN_TYPE_OBJECT_STR;
sym->bind = R_BIN_BIND_GLOBAL_STR;
@@ -313,7 +423,16 @@ static RList *symbols(RBinFile *bf) {
symbol->paddr = fi.offset;
symbol->vaddr = HBC_VADDR_BASE + fi.offset;
symbol->size = fi.size;
set_symbol_size (symbol, fi.size);
set_function_signature (symbol, &fi);
#if R2_ABIVERSION >= 139
if (fi.flags & HBC_FUNCTION_ASYNC) {
symbol->attr.flags |= R_BIN_ATTR_ASYNC;
}
if (fi.flags & HBC_FUNCTION_GENERATOR) {
symbol->attr.flags |= R_BIN_ATTR_GENERATOR;
}
#endif
symbol->ordinal = i;
symbol->type = R_BIN_TYPE_FUNC_STR;
symbol->bits = 32;
@@ -342,7 +461,7 @@ static RList *symbols(RBinFile *bf) {
r_bin_name_filtered (sym->name, nm);
sym->paddr = groups[i].paddr;
sym->vaddr = HBC_VADDR_BASE + groups[i].paddr;
sym->size = 1;
set_symbol_size (sym, 1);
sym->type = R_BIN_TYPE_OBJECT_STR;
sym->bits = 32;
r_list_append (symbols, sym);
@@ -503,6 +622,7 @@ static RList *strings_list(RBinFile *bf) {
ptr->vaddr = HBC_VADDR_BASE + meta.offset;
ptr->size = meta.isUTF16? (meta.length * 2): meta.length;
ptr->length = meta.length;
ptr->type = meta.isUTF16? R_STRING_TYPE_WIDE: R_STRING_TYPE_UTF8;
ptr->ordinal = i;
r_list_append (ret, ptr);
}
@@ -983,6 +1103,9 @@ const RBinPlugin r_bin_plugin_r2hermes = {
.baddr = &baddr,
.libs = &libs,
.lines = &lines,
#if R2_ABIVERSION >= 14
.trycatch = &trycatch,
#endif
.header = &header,
.fields = &fields,
#if R2_ABIVERSION >= 121
+159 -40
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,29 +1058,33 @@ 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[] =
"Usage: r2hermes[-arg] # see also pd:h for decompilation\n"
"r2hermes-h - help message (same as r2hermes-?, see pd:h? too)\n"
"r2hermes-A - Import symbols and entrypoints\n"
"r2hermes-AA - Also import strings and SLP literal comments/xrefs\n"
"r2hermes-AAA - Also analyze bytecode references\n"
"r2hermes-AAAA - Also perform full analysis (aaa)\n"
"r2hermes-E[jq] - List direct eval instruction sites (j=JSON, q=addresses only)\n"
"r2hermes-H - Show file information and hash status\n"
"r2hermes-L[?] - SLP literal cache: list/scan/reset/format/toggle\n"
"r2hermes-S[jr?] - emit SBOM from SLP literals (j=CycloneDX JSON, r=raw input)\n";
"r2hermes-S[jr?] - emit SBOM from SLP literals (j=CycloneDX JSON, r=raw input)\n"
"r2hermes-V[j?] - Show bytecode and React Native versions (HBC or libhermes.so)\n";
r_cons_print (core->cons, msg);
}
@@ -1098,7 +1102,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");
}
@@ -1148,11 +1151,127 @@ static void cmd_literals(HbcContext *ctx, RCore *core, const char *arg) {
}
#include "sbom.inc.c"
#include "version.inc.c"
static void analysis_xref(RCore *core, ut64 from, ut64 target, RAnalRefType type, bool va) {
if (target == UT64_MAX || target < HBC_VADDR_BASE) {
return;
}
if (!va) {
target -= HBC_VADDR_BASE;
}
if (r_io_is_valid_offset (core->io, target, 0)) {
r_anal_xrefs_set (core->anal, from, target, type);
}
}
/* Decode complete functions, including tails shorter than the maximum opcode
* size that the generic aar scan skips. Decode at vaddrs so absolute function
* and string operands from the architecture plugin share one address space. */
static void analyze_function_refs(RCore *core, HBC *hbc, u32 id, bool va) {
HBCFunc fi;
const u8 *code = NULL;
u32 size = 0;
if (hbc_get_function_info (hbc, id, &fi).code != RESULT_SUCCESS ||
hbc_get_function_bytecode (hbc, id, &code, &size).code != RESULT_SUCCESS || !code) {
return;
}
for (u32 pc = 0; pc < size && !r_cons_is_breaked (core->cons);) {
const ut64 vaddr = HBC_VADDR_BASE + fi.offset + pc;
const ut64 from = va? vaddr: (ut64)fi.offset + pc;
RAnalOp op = { 0 };
const int len = r_anal_op (core->anal, &op, vaddr, code + pc, R_MIN (size - pc, INT_MAX), R_ARCH_OP_MASK_BASIC);
if (len < 1 || (u32)len > size - pc) {
r_anal_op_fini (&op);
break;
}
analysis_xref (core, from, op.ptr, R_ANAL_REF_TYPE_DATA, va);
if (op.type == R_ANAL_OP_TYPE_CALL || op.type == R_ANAL_OP_TYPE_CCALL) {
analysis_xref (core, from, op.jump, R_ANAL_REF_TYPE_CALL, va);
} else if (op.type == R_ANAL_OP_TYPE_JMP || op.type == R_ANAL_OP_TYPE_CJMP) {
analysis_xref (core, from, op.jump, R_ANAL_REF_TYPE_CODE, va);
}
r_anal_op_fini (&op);
pc += len;
}
}
static void cmd_analyze(RCore *core, HbcContext *ctx, const char *arg) {
unsigned level = 0;
while (arg[level] == 'A') {
level++;
}
const char *rest = r_str_trim_head_ro (arg + level);
if (!strcmp (rest, "?")) {
r_cons_print (core->cons,
"Usage: r2hermes-A[AAA]\n"
" r2hermes-A Import symbols and entrypoints\n"
" r2hermes-AA Also import strings and SLP literal flags/comments/xrefs\n"
" r2hermes-AAA Also analyze references in every bytecode function\n"
" r2hermes-AAAA Also perform full analysis (aaa)\n"
"Levels are cumulative; addresses follow io.va.\n");
return;
}
if (level > 4 || *rest) {
R_LOG_ERROR ("Invalid analysis level. Use r2hermes-A? for help");
return;
}
HBC *hbc = NULL;
Result res = ensure_hbc_loaded (ctx, core, &hbc);
if (res.code != RESULT_SUCCESS) {
R_LOG_ERROR ("%s", safe_errmsg (res.error_message));
return;
}
const ut64 addr = core->addr;
const bool va = r_config_get_b (core->config, "io.va");
if (!r_flag_space_push (core->flags, R_FLAGS_FS_SYMBOLS)) {
return;
}
if (!r_core_bin_info (core, R_CORE_BIN_ACC_ENTRIES, NULL, R_MODE_SET, va, NULL, NULL) ||
r_core_cmd0 (core, ".is*") != 0) {
R_LOG_ERROR ("Failed to import HBC symbols and entrypoints");
goto done;
}
if (level >= 2) {
if (!r_core_bin_info (core, R_CORE_BIN_ACC_STRINGS, NULL, R_MODE_SET, va, NULL, NULL)) {
R_LOG_ERROR ("Failed to import HBC strings");
goto done;
}
res = hbc_literals_scan_code (hbc, NULL);
if (res.code != RESULT_SUCCESS) {
R_LOG_ERROR ("Literal scan failed: %s", safe_errmsg (res.error_message));
goto done;
}
r_flag_space_set (core->flags, R_FLAGS_FS_SYMBOLS);
register_all_artifacts (core, hbc);
}
if (level >= 3) {
const u32 count = hbc_function_count (hbc);
r_cons_break_push (core->cons, NULL, NULL);
for (u32 i = 0; i < count && !r_cons_is_breaked (core->cons); i++) {
analyze_function_refs (core, hbc, i, va);
}
const bool interrupted = r_cons_is_breaked (core->cons);
r_cons_break_pop (core->cons);
if (interrupted) {
goto done;
}
}
if (level >= 4) {
r_core_cmd0 (core, "aaa");
}
done:
r_flag_space_pop (core->flags);
r_core_seek (core, addr, true);
}
static void cmd_r2hermes(RCore *core, HbcContext *ctx, const char *arg) {
arg = r_str_trim_head_ro (arg);
if (*arg == '-') {
switch (arg[1]) {
case 'A':
cmd_analyze (core, ctx, arg + 1);
break;
case 'E':
cmd_list_eval_functions (ctx, core, arg + 2);
break;
@@ -1165,6 +1284,9 @@ static void cmd_r2hermes(RCore *core, HbcContext *ctx, const char *arg) {
case 'S':
cmd_sbom (ctx, core, arg + 2);
break;
case 'V':
cmd_version (ctx, core, arg + 2);
break;
case '?':
case 'h':
r2hermes_help (core);
@@ -1222,10 +1344,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;
@@ -1394,6 +1512,7 @@ R_API RLibStruct radare_plugin = {
.type = R_LIB_TYPE_CORE,
.data = (void *)&r_core_plugin_r2hermes,
.version = R2_VERSION,
.abiversion = R2_ABIVERSION
.abiversion = R2_ABIVERSION,
.pkgname = "r2hermes"
};
#endif
+7
View File
@@ -3,16 +3,23 @@
/* Core HBC plugin with plugin registration enabled for r2one */
#define HBC_CORE_REGISTER_PLUGINS 1
// radare2 defines R2_PLUGIN_INCORE when linking us statically (XPS)
#ifdef R2_PLUGIN_INCORE
#define HBC_STATIC_INCORE 1
#else
#define R2_PLUGIN_INCORE 1
#endif
#include "asm_hbc.c"
#include "bin_hbc.c"
#include "arch_hbc.c"
#undef HBC_VADDR_BASE
#include "core_hbc.c"
#ifndef HBC_STATIC_INCORE
R_API RLibStruct radare_plugin = {
.type = R_LIB_TYPE_CORE,
.data = (void *)&r_core_plugin_r2hermes,
.version = R2_VERSION,
.abiversion = R2_ABIVERSION
};
#endif
+13 -13
View File
@@ -84,9 +84,9 @@ static void set_esil(RAnalOp *op, const u8 *bytes, ut64 addr) {
case OP_RShift: E3 (">>")
case OP_URshift: E3 ("0x1f,&,>>")
case OP_Eq:
case OP_StrictEq: E3 ("==")
case OP_StrictEq: E3 ("==,$z")
case OP_Neq:
case OP_StrictNeq: E3 ("==,!")
case OP_StrictNeq: E3 ("==,$z,!")
case OP_Less: E3 ("<")
case OP_Greater: E3 (">")
case OP_LessEq: E3 ("<=")
@@ -184,10 +184,10 @@ static void set_esil(RAnalOp *op, const u8 *bytes, ut64 addr) {
r_strbuf_setf (&op->esil, "%u,r%u,=", r_read_le32 (bytes + 2), bytes[1]);
break;
case OP_LoadParam:
r_strbuf_setf (&op->esil, "arg%u,r%u,=", bytes[2], bytes[1]);
r_strbuf_setf (&op->esil, "%u,HBC_LOAD_PARAM,r%u,=", bytes[2], bytes[1]);
break;
case OP_LoadParamLong:
r_strbuf_setf (&op->esil, "arg%u,r%u,=", r_read_le32 (bytes + 2), bytes[1]);
r_strbuf_setf (&op->esil, "%u,HBC_LOAD_PARAM,r%u,=", r_read_le32 (bytes + 2), bytes[1]);
break;
case OP_NewArray:
r_strbuf_setf (&op->esil, "%u,NEWARR,r%u,=", r_read_le16 (bytes + 2), bytes[1]);
@@ -229,7 +229,7 @@ static void set_esil(RAnalOp *op, const u8 *bytes, ut64 addr) {
r_strbuf_setf (&op->esil, "r%u,THROW", bytes[1]);
break;
case OP_ThrowIfUndefinedInst:
r_strbuf_setf (&op->esil, "r%u,UNDEFINED,==,?{,THROW,}", bytes[1]);
r_strbuf_setf (&op->esil, "r%u,UNDEFINED,==,$z,?{,THROW,}", bytes[1]);
break;
case OP_IteratorClose:
r_strbuf_setf (&op->esil, "r%u,ITERCLOSE", bytes[1]);
@@ -276,14 +276,14 @@ static void set_esil(RAnalOp *op, const u8 *bytes, ut64 addr) {
case OP_JNotGreaterEqualN: JCC_S ("<")
case OP_JNotGreaterEqualLong:
case OP_JNotGreaterEqualNLong: JCC_L ("<")
case OP_JEqual: JCC_S ("==")
case OP_JEqualLong: JCC_L ("==")
case OP_JNotEqual: JCC_S ("==,!")
case OP_JNotEqualLong: JCC_L ("==,!")
case OP_JStrictEqual: JCC_S ("==")
case OP_JStrictEqualLong: JCC_L ("==")
case OP_JStrictNotEqual: JCC_S ("==,!")
case OP_JStrictNotEqualLong: JCC_L ("==,!")
case OP_JEqual: JCC_S ("==,$z")
case OP_JEqualLong: JCC_L ("==,$z")
case OP_JNotEqual: JCC_S ("==,$z,!")
case OP_JNotEqualLong: JCC_L ("==,$z,!")
case OP_JStrictEqual: JCC_S ("==,$z")
case OP_JStrictEqualLong: JCC_L ("==,$z")
case OP_JStrictNotEqual: JCC_S ("==,$z,!")
case OP_JStrictNotEqualLong: JCC_L ("==,$z,!")
/* aggregate / opaque ops with no precise ESIL model */
case OP_Unreachable:
+145
View File
@@ -0,0 +1,145 @@
/* r2hermes - BSD - Copyright 2026 - pancake */
/* r2hermes-V: identify the Hermes / React Native build of the loaded binary.
*
* For HBC files the bytecode version comes from the header. For the native
* engine (libhermes.so) it recovers:
* - the "for RN x.y.z" string Hermes reports as "OSS Release Version"
* - the constant returned by HermesRuntime::getBytecodeVersion ()
* - the toolchain recorded in .comment (via RBinInfo.compiler)
*/
#include <hbc/parser.h>
#define HERMES_RN_TAG "for RN "
#define HERMES_BCVER_SYM "_ZN8facebook6hermes13HermesRuntime18getBytecodeVersionEv"
static char *version_find_rn(RBinFile *bf) {
ut64 size = 0;
const ut8 *data = r_buf_data (bf->buf, &size);
if (!data || size > ST32_MAX) {
return NULL;
}
const int taglen = strlen (HERMES_RN_TAG);
const ut8 *p = data;
const ut8 *end = data + size;
while ((p = r_mem_mem (p, (int) (end - p), (const ut8 *)HERMES_RN_TAG, taglen))) {
const ut8 *v = p + taglen;
const ut8 *q = v;
while (q < end && (isdigit (*q) || *q == '.' || *q == '-' || isalpha (*q))) {
q++;
}
// must be a standalone NUL-terminated string starting with a digit
if (q > v && q < end && !*q && isdigit (*v) && (p == data || !p[-1])) {
return r_str_ndup ((const char *)v, (int) (q - v));
}
p++;
}
return NULL;
}
static int version_find_bcver(RCore *core) {
RVecRBinSymbol *syms = r_bin_get_symbols_vec (core->bin);
if (!syms) {
return -1;
}
ut64 addr = UT64_MAX;
RBinSymbol *sym;
R_VEC_FOREACH (syms, sym) {
if (sym->name && sym->vaddr && r_str_startswith (r_str_get (sym->name->oname), HERMES_BCVER_SYM)) {
addr = sym->vaddr;
break;
}
}
if (addr == UT64_MAX) {
return -1;
}
ut8 buf[64];
if (!r_io_read_at (core->io, addr, buf, sizeof (buf))) {
return -1;
}
// the function just returns a constant; accept a short prologue before it
int ver = -1;
int off = 0;
int i;
for (i = 0; i < 8 && off < (int)sizeof (buf); i++) {
RAnalOp op;
r_anal_op_init (&op);
int len = r_anal_op (core->anal, &op, addr + off, buf + off, sizeof (buf) - off, R_ARCH_OP_MASK_VAL);
const ut32 type = op.type & R_ANAL_OP_TYPE_MASK;
if (type == R_ANAL_OP_TYPE_MOV && op.val != UT64_MAX && op.val > 0 && op.val < 1000) {
ver = (int)op.val;
}
r_anal_op_fini (&op);
if (len < 1 || type == R_ANAL_OP_TYPE_RET) {
break;
}
off += len;
}
return ver;
}
static void cmd_version(HbcContext *ctx, RCore *core, const char *arg) {
const bool json = *arg == 'j';
if (*arg && !json) {
r_cons_print (core->cons,
"Usage: r2hermes-V[j]\n"
" r2hermes-V Show Hermes bytecode and React Native versions\n"
" r2hermes-Vj Same as JSON\n");
return;
}
RBinFile *bf = core->bin->cur;
if (!bf || !bf->buf) {
R_LOG_ERROR ("No binary loaded");
return;
}
int bcver = -1;
char *rn = NULL;
const char *compiler = NULL;
const char *kind = "native";
HBCHeader header;
ut8 magic[8];
if (r_buf_read_at (bf->buf, 0, magic, sizeof (magic)) == sizeof (magic) && r_read_le64 (magic) == HEADER_MAGIC && hbc_load_current_binary (ctx, core).code == RESULT_SUCCESS && hbc_get_header (ctx->hbc, &header).code == RESULT_SUCCESS) {
kind = "hbc";
bcver = header.version;
} else {
rn = version_find_rn (bf);
bcver = version_find_bcver (core);
RBinInfo *info = r_bin_get_info (core->bin);
compiler = info? info->compiler: NULL;
if (!rn && bcver < 0) {
R_LOG_WARN ("This does not look like a Hermes binary");
}
}
RCons *cons = core->cons;
if (json) {
PJ *pj = r_core_pj_new (core);
pj_o (pj);
pj_ks (pj, "type", kind);
if (bcver >= 0) {
pj_kn (pj, "bytecode_version", bcver);
}
if (rn) {
pj_ks (pj, "react_native", rn);
}
if (compiler) {
pj_ks (pj, "compiler", compiler);
}
pj_end (pj);
char *s = pj_drain (pj);
r_cons_println (cons, s);
free (s);
} else {
r_cons_printf (cons, "type %s\n", kind);
if (bcver >= 0) {
r_cons_printf (cons, "bytecode_version %d\n", bcver);
}
if (rn) {
r_cons_printf (cons, "react_native %s\n", rn);
}
if (compiler) {
r_cons_printf (cons, "compiler %s\n", compiler);
}
}
free (rn);
}
+161
View File
@@ -0,0 +1,161 @@
NAME=r2hermes analysis help
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
r2hermes-?~r2hermes-A
r2hermes-A?
EOF
EXPECT=<<EOF
r2hermes-A - Import symbols and entrypoints
r2hermes-AA - Also import strings and SLP literal comments/xrefs
r2hermes-AAA - Also analyze bytecode references
r2hermes-AAAA - Also perform full analysis (aaa)
Usage: r2hermes-A[AAA]
r2hermes-A Import symbols and entrypoints
r2hermes-AA Also import strings and SLP literal flags/comments/xrefs
r2hermes-AAA Also analyze references in every bytecode function
r2hermes-AAAA Also perform full analysis (aaa)
Levels are cumulative; addresses follow io.va.
EOF
RUN
NAME=r2hermes-A imports symbols and preserves session state
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
f-*
fs scratch
s 0x100000b4
r2hermes-A
?v $$
fs~scratch[1]
fs *
f~global,entry,str.
EOF
EXPECT=<<EOF
0x100000b4
*
0x100000b0 1 entry0
0x100000b0 10 sym.global_0x000000b0
EOF
RUN
NAME=r2hermes-AA adds strings
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
f-*
r2hermes-AA
f~global,entry,str.
EOF
EXPECT=<<EOF
0x1000009c 6 str.global
0x100000a2 11 str.print_123__
0x100000b0 1 entry0
0x100000b0 10 sym.global_0x000000b0
EOF
RUN
NAME=r2hermes-AA imports literals again from an existing cache
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
r2hermes-Lq~^0x10037b28
ax-*
f- lit.*
CC- @ 0x10037b28
r2hermes-AA
f~lit.obj.0x37b28
CC. @ 0x10037b28
axtq @ 0x10037b28
r2hermes-AA
axtq @ 0x10037b28
EOF
EXPECT=<<EOF
0x10037b28 {trace: 0, info: 1, warn: 2, error: 3}
0x10037b28 1 lit.obj.0x37b28
{trace: 0, info: 1, warn: 2, error: 3}
0x1003e9f9
0x1003e9f9
EOF
RUN
NAME=r2hermes-AA imports literal physical addresses
FILE=bins/hbc/index.android.bundle
ARGS=-e io.va=false
CMDS=<<EOF
f-*
r2hermes-AA
f~lit.obj.0x37b28
CC. @ 0x37b28
axtq @ 0x37b28
EOF
EXPECT=<<EOF
0x00037b28 1 lit.obj.0x37b28
{trace: 0, info: 1, warn: 2, error: 3}
0x3e9f9
EOF
RUN
NAME=r2hermes-AAA finds short function references and remains additive
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
ax-*
r2hermes-AA
axtq @ 0x100000a2
r2hermes-AAA
axtq @ 0x100000a2
r2hermes-AAA
axtq @ 0x100000a2
EOF
EXPECT=<<EOF
0x100000b0
0x100000b0
EOF
RUN
NAME=r2hermes-AAA uses physical addresses for code and data
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e io.va=false
CMDS=<<EOF
ax-*
r2hermes-AAA
axtq @ 0xa2
axtq @ 0x100000a2
f~global,entry,str.print
EOF
EXPECT=<<EOF
0xb0
0x0000009c 6 str.global
0x000000a2 11 str.print_123__
0x000000b0 1 entry0
0x000000b0 10 sym.global_0x000000b0
EOF
RUN
NAME=r2hermes-AAAA analyzes function control flow
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e log.level=0
CMDS=<<EOF
r2hermes-AAAA
aflj~{[0].addr}
aflj~{[0].size}
EOF
EXPECT=<<EOF
268435632
10
EOF
RUN
NAME=r2hermes analysis rejects invalid levels before importing
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
f-*
r2hermes-AAAAA
r2hermes-AAj
r2hermes-AA extra
f
EOF
EXPECT=
EXPECT_ERR=<<EOF
ERROR: Invalid analysis level. Use r2hermes-A? for help
ERROR: Invalid analysis level. Use r2hermes-A? for help
ERROR: Invalid analysis level. Use r2hermes-A? for help
EOF
RUN
+1 -3
View File
@@ -415,9 +415,7 @@ RUN
NAME=pd:hc short-circuit OR merges consecutive same-target conditional gotos
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
pd:hc 730~ || r2===r20 ||
EOF
CMDS=pd:hc 730~ \|\| r2===r20 \|\|
EXPECT=<<EOF
if (r1===r20 || r2===r20 || r3===r20 || r4===r20) goto loc_00055f28;
EOF
+115
View File
@@ -84,6 +84,77 @@ EXPECT=<<EOF
EOF
RUN
NAME=hbc function signature metadata
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e asm.sub.varmin=16
CMDS=<<EOF
isj~{[0].arg_count}
isj~{[0].cc_arg_count}
isj~{[0].ret_count}
EOF
EXPECT=<<EOF
0
1
1
EOF
RUN
NAME=hbc register profile has no fake ABI roles
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e asm.sub.varmin=16
CMDS=<<EOF
drpj~{alias_info}
EOF
EXPECT=<<EOF
[{"role":0,"role_str":"PC","reg":"pc"}]
EOF
RUN
NAME=hbc LoadParam ESIL uses a frame slot instead of a fake register
FILE=bins/hbc/index.android.bundle
ARGS=-e asm.sub.varmin=16
CMDS=<<EOF
aoj 1 @ 0x1003e3f7~{[0].esil}
EOF
EXPECT=<<EOF
1,HBC_LOAD_PARAM,r8,=
EOF
RUN
NAME=hbc equality branch ESIL materializes the zero comparison
FILE=bins/hbc/index.android.bundle
ARGS=-e asm.sub.varmin=16
CMDS=<<EOF
aoj 1 @ 0x1003e5fc~{[0].esil}
EOF
EXPECT=<<EOF
r1,r3,==,$z,!,?{,0x1003e637,pc,=,}
EOF
RUN
NAME=hbc SwitchImm analysis exposes jump table metadata
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
af @ 0x1004d09a
agfj @ 0x1004d09a~{[0].blocks[0].switchop}
EOF
EXPECT=<<EOF
{"addr":268751010,"defval":268751197,"maxval":27,"minval":0,"daddr":268751204,"dsize":4,"amount":28,"cases":[{"addr":268751204,"value":0,"jump":268751078},{"addr":268751208,"value":1,"jump":268751028},{"addr":268751212,"value":2,"jump":268751078},{"addr":268751224,"value":5,"jump":268751172},{"addr":268751248,"value":11,"jump":268751050},{"addr":268751256,"value":13,"jump":268751124},{"addr":268751264,"value":15,"jump":268751078},{"addr":268751268,"value":16,"jump":268751148},{"addr":268751280,"value":19,"jump":268751100},{"addr":268751308,"value":26,"jump":268751172},{"addr":268751312,"value":27,"jump":268751172}]}
EOF
RUN
NAME=hbc exception handlers are exposed through bin metadata
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
iw*~try.0
EOF
EXPECT=<<EOF
f try.0.1003e6dc.from=0x1003e703
f try.0.1003e6dc.to=0x1003e713
f try.0.1003e6dc.catch=0x1003e715
EOF
RUN
NAME=hbc entry point (va)
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e asm.sub.varmin=16
@@ -182,6 +253,50 @@ EXPECT=<<EOF
EOF
RUN
NAME=hbc structured operands
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
aoj 3 @ entry0~{0.opex}
aoj 3 @ entry0~{1.opex}
aoj 3 @ entry0~{2.opex}
EOF
EXPECT=<<EOF
{"operands":[{"type":"reg","value":"r0"},{"type":"imm","value":1,"kind":"string_id","address":268435618,"byte_length":11}]}
{"operands":[{"type":"reg","value":"r0"},{"type":"reg","value":"r0"},{"type":"imm","value":0}]}
{"operands":[{"type":"reg","value":"r0"}]}
EOF
RUN
NAME=hbc structured empty string length
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
aoj 1 @ 0x1003e45b~{0.opex}
EOF
EXPECT=<<EOF
{"operands":[{"type":"reg","value":"r9"},{"type":"imm","value":2976,"kind":"string_id","address":268560336,"byte_length":0}]}
EOF
RUN
NAME=hbc structured function operand
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
aoj 1 @ 0x1003a3bb~{0.opex}
EOF
EXPECT=<<EOF
{"operands":[{"type":"reg","value":"r1"},{"type":"reg","value":"r3"},{"type":"imm","value":1,"kind":"function_id","address":268690421}]}
EOF
RUN
NAME=hbc implicit call frame operands
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
aoj 1 @ 0x100895ac~{0.opex}
EOF
EXPECT=<<EOF
{"operands":[{"type":"reg","value":"r1"},{"type":"reg","value":"r5"},{"type":"imm","value":5},{"type":"reg","value":"r10","implicit":true,"kind":"call_this"},{"type":"reg","value":"r9","implicit":true,"kind":"call_arg","index":0},{"type":"reg","value":"r8","implicit":true,"kind":"call_arg","index":1},{"type":"reg","value":"r7","implicit":true,"kind":"call_arg","index":2},{"type":"reg","value":"r6","implicit":true,"kind":"call_arg","index":3}]}
EOF
RUN
NAME=hbc sections (va)
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e asm.sub.varmin=16
+13
View File
@@ -81,3 +81,16 @@ EXPECT=<<EOF
{"magic":2240826417119764422,"version":96,"sourceHash":"a97c8302dab90bec7184a9183b22b43badd57a65","fileLength":292,"globalCodeIndex":0,"functionCount":1,"stringKindCount":1,"identifierCount":0,"stringCount":2,"overflowStringCount":0,"stringStorageSize":17,"bigIntCount":0,"bigIntStorageSize":0,"regExpCount":0,"regExpStorageSize":0,"literalValueBufferSize":0,"arrayBufferSize":0,"objKeyBufferSize":0,"objValueBufferSize":0,"objShapeTableCount":0,"numStringSwitchImms":0,"segmentID":0,"cjsModuleCount":0,"functionSourceCount":0,"debugInfoOffset":200,"staticBuiltins":false,"cjsModulesStaticallyResolved":false,"hasAsync":false}
EOF
RUN
NAME=r2hermes-V reports the HBC bytecode version
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
r2hermes-V
r2hermes-Vj
EOF
EXPECT=<<EOF
type hbc
bytecode_version 96
{"type":"hbc","bytecode_version":96}
EOF
RUN
+59
View File
@@ -0,0 +1,59 @@
NAME=hbc literal addresses and constructor refs
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
r2hermes-L~trace:
r2hermes-Lx 0x10037b3c
r2hermes-Lr 0x10037b3c~^axd
ax-*
.r2hermes-Lr 0x10037b3c
axtq @ 0x10037b3c
EOF
EXPECT=<<EOF
object n=4 id=(0,0) vaddr=0x10037b28 xrefs=1 0x1003e9f9 {trace: 0, info: 1, warn: 2, error: 3}
0x10037b3c 0x1003eb09 0x1003ebe7
0x10037b3c 0x100c2cfb
axd 0x10037b3c 0x1003eb09
axd 0x10037b3c 0x1003ebe7
axd 0x10037b3c 0x100c2cfb
0x1003eb09
0x1003ebe7
0x100c2cfb
EOF
RUN
NAME=hbc literal physical addresses and refs
FILE=bins/hbc/index.android.bundle
ARGS=-e io.va=false
CMDS=<<EOF
r2hermes-L~trace:
r2hermes-Lq~^0x37b28
r2hermes-Lx 0x37b3c
r2hermes-Lr 0x37b3c~^axd
axtq @ 0x37b3c
EOF
EXPECT=<<EOF
object n=4 id=(0,0) paddr=0x00037b28 xrefs=1 0x3e9f9 {trace: 0, info: 1, warn: 2, error: 3}
0x37b28 {trace: 0, info: 1, warn: 2, error: 3}
0x37b3c 0x3eb09 0x3ebe7
0x37b3c 0xc2cfb
axd 0x37b3c 0x3eb09
axd 0x37b3c 0x3ebe7
axd 0x37b3c 0xc2cfb
0x3eb09
0x3ebe7
0xc2cfb
EOF
RUN
NAME=hbc literal help drops deprecated alias
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
r2hermes-L?~pd:hL
pd:h?~pd:hL
pd:hL?
EOF
EXPECT=
EXPECT_ERR=<<EOF
ERROR: Unknown subcommand. Use pd:h? for help
EOF
RUN
+124 -1
View File
@@ -1,6 +1,7 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include "../src/lib/hbc_internal.h"
#include <hbc/opcodes.h>
#include <stdio.h>
#include <string.h>
@@ -18,6 +19,9 @@ static int test_small_debug_info(const char *root) {
HBC *hbc = NULL;
CHECK (hbc_open (path, &hbc).code == RESULT_SUCCESS);
u32 frame_size = 0;
CHECK (hbc_get_function_frame_size (hbc, 0, &frame_size).code == RESULT_SUCCESS);
CHECK (frame_size == hbc->reader.function_headers[0].frameSize);
HBCDebugInfo di = { 0 };
CHECK (hbc_get_debug_info (hbc, &di).code == RESULT_SUCCESS);
@@ -74,6 +78,81 @@ static int test_empty_debug_info(const char *root) {
return 0;
}
static int test_exception_handlers(const char *root) {
char path[512];
snprintf (path, sizeof (path), "%s/test/bins/hbc/index.android.bundle", root);
HBC *hbc = NULL;
CHECK (hbc_open (path, &hbc).code == RESULT_SUCCESS);
u32 handler_count = 0;
u32 generator_count = 0;
for (u32 function_id = 0; function_id < hbc_function_count (hbc); function_id++) {
HBCFunc function;
CHECK (hbc_get_function_info (hbc, function_id, &function).code == RESULT_SUCCESS);
CHECK (!(function.flags & HBC_FUNCTION_ASYNC));
if (function.flags & HBC_FUNCTION_GENERATOR) {
generator_count++;
}
HBCExceptionHandlerArray handlers = { 0 };
CHECK (hbc_get_function_exception_handlers (hbc, function_id, &handlers).code == RESULT_SUCCESS);
for (u32 i = 0; i < handlers.count; i++) {
CHECK (handlers.handlers[i].start < handlers.handlers[i].end);
CHECK (handlers.handlers[i].end <= function.size);
CHECK (handlers.handlers[i].target < function.size);
}
handler_count += handlers.count;
hbc_free_exception_handlers (&handlers);
}
CHECK (handler_count > 0);
CHECK (generator_count > 0);
hbc_close (hbc);
return 0;
}
static const Instruction *find_instruction(HBCISA isa, const char *name) {
for (u32 i = 0; i < isa.count; i++) {
if (isa.instructions[i].name && !strcmp (isa.instructions[i].name, name)) {
return &isa.instructions[i];
}
}
return NULL;
}
static int test_opcode_operand_meanings(void) {
static const int versions[] = {
51, 58, 59, 61, 62, 68, 69, 70, 72, 73, 76, 80, 81, 82,
83, 84, 85, 86, 87, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99
};
static const char *function_ops[] = {
"call_direct", "call_direct_long_index",
"create_closure", "create_closure_long_index",
"create_generator_closure", "create_generator_closure_long_index",
"create_async_closure", "create_async_closure_long_index",
"create_generator", "create_generator_long_index"
};
static const char *builtin_ops[] = {
"call_builtin", "call_builtin_long", "get_builtin_closure",
"jmp_builtin_is", "jmp_builtin_is_long",
"jmp_builtin_is_not", "jmp_builtin_is_not_long"
};
for (size_t i = 0; i < sizeof (versions) / sizeof (versions[0]); i++) {
HBCISA isa = hbc_isa_getv (versions[i]);
for (size_t j = 0; j < sizeof (function_ops) / sizeof (function_ops[0]); j++) {
const Instruction *insn = find_instruction (isa, function_ops[j]);
if (insn) {
CHECK (insn->operands[2].operand_meaning == OPERAND_MEANING_FUNCTION_ID);
}
}
for (size_t j = 0; j < sizeof (builtin_ops) / sizeof (builtin_ops[0]); j++) {
const Instruction *insn = find_instruction (isa, builtin_ops[j]);
if (insn) {
CHECK (insn->operands[1].operand_meaning == OPERAND_MEANING_BUILTIN_ID);
}
}
}
return 0;
}
static int test_function_bytecode_bounds(const char *root) {
char path[512];
snprintf (path, sizeof (path), "%s/test/bins/hbc/bespoke_eval.hbc", root);
@@ -130,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_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;