Compare commits

..
1 Commits
Author SHA1 Message Date
pancake 991cc90760 Rename the cli tool to r2hermes instead of libhbctool 2026-07-03 20:51:19 +02:00
87 changed files with 3331 additions and 4256 deletions

No files matched your search

+3
View File
@@ -4,5 +4,8 @@ updates:
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "##build "
prefix-development: "##build "
labels:
- buildsystem
+17 -68
View File
@@ -1,8 +1,5 @@
name: CI
env:
R2V: 6.2.6
permissions:
contents: write
@@ -19,18 +16,16 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
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@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build library
run: make
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
- name: Build r2 plugin
run: make r2
- name: Run testsuite
@@ -57,7 +52,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -66,15 +61,13 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
uses: radareorg/github-actions@v2
- name: Build Debian package
run: make -C dist/debian SUDO=
- name: Upload Debian package artifact
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
uses: actions/upload-artifact@v7
with:
name: debian-package
path: dist/debian/*.deb
@@ -86,10 +79,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@v6
with:
python-version: 3.12.x
@@ -100,9 +93,7 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
uses: radareorg/github-actions@v2
- name: Build and install with Meson
shell: cmd
@@ -131,56 +122,22 @@ 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@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
uses: actions/upload-artifact@v7
with:
name: windows-package
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
path: r2hermes-windows-x64.zip
if-no-files-found: error
release:
name: Create release and upload artifacts
runs-on: ubuntu-latest
needs: [build, debian, windows, bindist]
needs: [build, debian, windows]
if: needs.build.outputs.tag != ''
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -192,33 +149,25 @@ jobs:
ls -l libhbc*
- name: Download Debian package artifact
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
uses: actions/download-artifact@v8
with:
name: debian-package
path: release-artifacts
- name: Download Windows package artifact
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
uses: actions/download-artifact@v8
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/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
uses: softprops/[email protected].1
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 }}
+2 -7
View File
@@ -1,8 +1,5 @@
name: Meson Build
env:
R2V: 6.2.2
on:
push:
workflow_dispatch: {}
@@ -14,7 +11,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -23,9 +20,7 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
uses: radareorg/github-actions@v2
- name: Build library and r2 plugin with meson
run: meson setup builddir && meson compile -C builddir
+6 -3
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Checkout TinyCC repository
@@ -30,11 +30,12 @@ jobs:
run: |
git clone --depth=1 https://github.com/radareorg/radare2.git
cd radare2
export CC=tcc
echo core.r2hermes > libr/xps/static.cfg
export CC=tcc
cp -f ../r2plugin/config.mk libr/xps/config.mk
git clone .. libr/xps/p/r2hermes
make -C libr/xps/p/r2hermes include/hbc/version.h
make -C libr/xps/p/r2hermes
make -C libr/xps
./configure --prefix=/usr --with-compiler=tcc
make -j
@@ -49,7 +50,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Install build dependencies
@@ -63,6 +64,8 @@ jobs:
# Clone r2hermes into the plugins directory and configure XPS
cp -f ../r2plugin/config.mk libr/xps/config.mk
git clone .. libr/xps/p/r2hermes
make -C libr/xps/p/r2hermes include/hbc/version.h
make -C libr/xps/p/r2hermes
make -C libr/xps
# Build radare2 with meson
+4 -9
View File
@@ -1,8 +1,5 @@
name: Windows Build
env:
R2V: 6.2.2
on:
push:
workflow_dispatch: {}
@@ -14,10 +11,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v7
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
uses: actions/setup-python@v6
with:
python-version: 3.12.x
@@ -28,9 +25,7 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
uses: radareorg/github-actions@v2
- name: Build and install with Meson
shell: cmd
@@ -60,7 +55,7 @@ jobs:
Compress-Archive -Path install\* -DestinationPath r2hermes-windows-x64.zip -Force
- name: Upload artifacts
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
uses: actions/upload-artifact@v7
with:
name: windows-package
path: r2hermes-windows-x64.zip
-2
View File
@@ -1,4 +1,3 @@
.pmai/
build/
bin/
third_party/
@@ -9,4 +8,3 @@ third_party/
test/bins
*.swp
include/hbc/version.h
*.zip
+3 -47
View File
@@ -1,9 +1,5 @@
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)
@@ -16,7 +12,6 @@ BUILD_DIR = build
BIN_DIR = bin
## Source files
R2_CFLAGS=$(shell pkg-config --cflags r_util 2>/dev/null || r2pm -H R2_CFLAGS 2>/dev/null || r2 -H R2_CFLAGS 2>/dev/null || echo "-I/usr/local/include/libr -I/usr/local/libr")
UTILS_SRC = $(wildcard $(SRC_DIR)/utils/*.c)
PARSERS_SRC = $(wildcard $(SRC_DIR)/parsers/*.c)
DISASM_SRC = $(wildcard $(SRC_DIR)/disassembly/*.c)
@@ -26,7 +21,6 @@ LIB_SRC = $(UTILS_SRC) $(PARSERS_SRC) $(DISASM_SRC) $(DECOMPILE_SRC) $(OPCODES_S
$(SRC_DIR)/hbc.c $(SRC_DIR)/literals_api.c $(SRC_DIR)/opcodes/encoder.c $(SRC_DIR)/opcodes/decoder.c $(SRC_DIR)/r2.c
MAIN_SRC = src/tool/r2hermes.c
HBC_HEADERS = $(shell find include -name '*.h')
CFLAGS+=${R2_CFLAGS}
## Object files
LIB_OBJ = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(LIB_SRC))
@@ -70,7 +64,7 @@ $(MAIN_RELOC_OBJ): $(MAIN_OBJ) $(LIB_OBJ)
$(CC) $(LD_RELOC_FLAGS) -o $@ $^
$(BIN_FILE): $(STATIC_LIB) $(MAIN_RELOC_OBJ) | $(shell mkdir -p $(BIN_DIR))
$(CC) $(CFLAGS) -o $@ $(MAIN_RELOC_OBJ) $(R2_LDFLAGS) -lr_util
$(CC) $(CFLAGS) -o $@ $(MAIN_RELOC_OBJ)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(HBC_HEADERS) | $(VH)
@mkdir -p $(dir $@)
@@ -90,7 +84,7 @@ TEST_SRC = $(wildcard $(TEST_DIR)/*.c)
TEST_BIN = $(BIN_DIR)/run_tests
$(TEST_BIN): $(STATIC_LIB) $(TEST_SRC) | $(shell mkdir -p $(BIN_DIR))
$(CC) $(CFLAGS) -fvisibility=default $(INCLUDES) -o $@ $(TEST_SRC) -L$(BUILD_DIR) -lhbc $(R2_LDFLAGS) -lr_util
$(CC) $(CFLAGS) -fvisibility=default $(INCLUDES) -o $@ $(TEST_SRC) -L$(BUILD_DIR) -lhbc
test: $(BIN_FILE) $(TEST_BIN)
$(TEST_BIN)
@@ -103,7 +97,6 @@ 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:
@@ -118,43 +111,6 @@ 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' > $@
@@ -164,4 +120,4 @@ $(VH):
echo '#define LIBHBC_VERSION_PATCH "$(shell echo $(VERSION) | cut -d . -f 3)"' >> $@
echo '#endif' >> $@
.PHONY: r2 test test2 all clean debug asan bindist
.PHONY: r2 test test2 all clean debug asan
-18
View File
@@ -81,30 +81,12 @@ 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 -6
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_BYTECODE_H
#define LIBHBC_BYTECODE_H
@@ -7,9 +7,6 @@
#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;
@@ -46,7 +43,7 @@ void _hbc_parsed_instruction_list_free(ParsedInstructionList *list);
Result _hbc_parse_instruction(HBCReader *reader, FunctionHeader *function_header, u32 offset, ParsedInstruction *out_instruction);
Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedInstructionList *out_instructions, HBCISA isa);
Result _hbc_instruction_to_string(ParsedInstruction *instruction, RStrBuf *out_string);
Result _hbc_instruction_to_string(ParsedInstruction *instruction, StringBuffer *out_string);
/* Raw value of the i-th operand (0..5); 0 when the index is out of range. */
u32 hbc_operand_value(const ParsedInstruction *insn, int i);
@@ -63,7 +60,7 @@ BytecodeModule *_hbc_get_bytecode_module(u32 bytecode_version);
const char **_hbc_get_builtin_functions(BytecodeModule *module, u32 *out_count);
/* Opcode handlers (defined in version-specific modules) */
typedef Result(*OpcodeHandler)(HBCReader *reader, RBuffer *bytecode, ParsedInstruction *out_instruction);
typedef Result(*OpcodeHandler)(HBCReader *reader, BufferReader *bytecode, ParsedInstruction *out_instruction);
/* Helpers to classify opcodes */
bool _hbc_is_jump_instruction(u8 opcode);
+51 -19
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_COMMON_H
#define LIBHBC_COMMON_H
@@ -11,19 +11,23 @@
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <r_types.h>
#include <r_list.h>
#include <r_util/r_buf.h>
#include <r_util/r_strbuf.h>
#if defined(_WIN32) && defined(HBC_BUILD_SHARED)
#define HBC_API __declspec (dllexport)
#define HBC_API __declspec(dllexport)
#elif defined(__GNUC__) || defined(__clang__)
#define HBC_API __attribute__((visibility ("default")))
#define HBC_API __attribute__ ((visibility ("default")))
#else
#define HBC_API
#endif
#ifndef R2_VERSION
#define R_RETURN_VAL_IF_FAIL(x, y) \
if (! (x)) \
return y
#define R_LOG_WARN(x, ...) // (x)
#define R_LOG_DEBUG(x, ...) // (x)
#endif
/* Debug logging support - disable by default for clean output */
#ifndef HBC_DEBUG_LOGGING
#define HBC_DEBUG_LOGGING 0
@@ -51,6 +55,13 @@ typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
/* String buffer for dynamic strings */
typedef struct {
char *data;
size_t length;
size_t capacity;
} StringBuffer;
/* Result code for error handling */
typedef enum {
RESULT_SUCCESS,
@@ -72,31 +83,52 @@ typedef struct {
const char *error_message;
} Result;
/* Buffer reader for file operations */
typedef struct {
uint8_t *data;
size_t size;
size_t position;
} BufferReader;
/* Forward declarations */
struct HBCReader;
typedef struct HBCReader HBCReader;
#define SUCCESS_RESULT() ((Result){ RESULT_SUCCESS, "" })
/* StringBuffer functions */
Result _hbc_sb_init(StringBuffer *buffer, size_t initial_capacity);
Result _hbc_sb_append(StringBuffer *buffer, const char *str);
Result _hbc_sb_appendf(StringBuffer *buffer, const char *fmt, ...);
Result _hbc_sb_append_char(StringBuffer *buffer, char c);
Result _hbc_sb_append_int(StringBuffer *buffer, int value);
void _hbc_sb_free(StringBuffer *buffer);
#define ERROR_RESULT(code, message) ((Result){ code, message })
/* BufferReader functions */
Result _hbc_buffer_reader_init_from_file(BufferReader *reader, const char *filename);
Result _hbc_buffer_reader_init_from_memory(BufferReader *reader, const u8 *data, size_t size);
Result _hbc_buffer_reader_read_u8(BufferReader *reader, u8 *out_value);
Result _hbc_buffer_reader_read_u16(BufferReader *reader, u16 *out_value);
Result _hbc_buffer_reader_read_u32(BufferReader *reader, u32 *out_value);
Result _hbc_buffer_reader_read_u64(BufferReader *reader, u64 *out_value);
Result _hbc_buffer_reader_read_bytes(BufferReader *reader, u8 *out_buffer, size_t length);
Result _hbc_buffer_reader_seek(BufferReader *reader, size_t position);
Result _hbc_buffer_reader_align(BufferReader *reader, size_t alignment);
void _hbc_buffer_reader_free(BufferReader *reader);
static inline Result hbc_result_from_result(Result result) {
return result;
}
static inline Result hbc_result_from_bool(bool ok) {
return ok? SUCCESS_RESULT (): ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "String buffer operation failed");
}
#define HBC_TO_RESULT(expr) _Generic ((expr), Result: hbc_result_from_result, bool: hbc_result_from_bool) (expr)
/* String case conversion utilities */
HBC_API void hbc_camel_to_snake(const char *camel, char *snake, size_t snake_size);
HBC_API void hbc_snake_to_camel(const char *snake, char *camel, size_t camel_size);
/* Utility macros for error handling */
#define RETURN_IF_ERROR(expr) \
do { \
Result result = HBC_TO_RESULT (expr); \
Result result = (expr); \
if (result.code != RESULT_SUCCESS) { \
return result; \
} \
} while (0)
#define SUCCESS_RESULT() ((Result){ RESULT_SUCCESS, "" })
#define ERROR_RESULT(code, message) ((Result){ code, message })
#endif /* LIBHBC_COMMON_H */
+58 -53
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_DECOMPILER_H
#define LIBHBC_DECOMPILER_H
@@ -8,46 +8,17 @@
#include <hbc/bytecode.h>
#include <hbc/hbc.h>
#include <hbc/decompilation/token.h>
#include <r_vec.h>
/* Forward declaration */
struct Environment;
typedef struct Environment Environment;
struct BasicBlock;
typedef struct BasicBlock BasicBlock;
typedef struct DowhileLoop {
u32 top;
u32 back_edge;
u32 guard_pos;
u32 exit_addr;
bool promoted;
/* For a materialized guard (`r=cmp; if (r){do}while (cmp)`), the while
* header renders this condition instead of the guard register. NULL when
* the guard already spells the condition (fused compare-jump). */
void *while_cond;
bool while_cond_invert;
} DowhileLoop;
typedef struct ForeverLoop {
u32 top;
u32 exit;
} ForeverLoop;
R_VEC_TYPE(RVecHBCU32, u32)
R_VEC_TYPE(RVecBasicBlockPtr, BasicBlock *)
R_VEC_TYPE(RVecEnvironmentPtr, Environment *)
R_VEC_TYPE(RVecDowhileLoop, DowhileLoop)
R_VEC_TYPE(RVecForeverLoop, ForeverLoop)
R_VEC_TYPE(RVecHbcTokenString, HbcTokenString)
/* Forward declaration */
struct DecompiledFunctionBody;
typedef struct DecompiledFunctionBody DecompiledFunctionBody;
/* Basic block for control flow */
struct BasicBlock {
typedef struct BasicBlock {
u32 start_address;
u32 end_address;
bool may_be_cycling_anchor;
@@ -59,15 +30,24 @@ struct BasicBlock {
bool is_conditional_jump_anchor;
bool is_switch_action_anchor;
ParsedInstruction *anchor_instruction;
RVecHBCU32 jump_targets;
u32 *jump_targets_for_anchor;
u32 jump_targets_count;
bool stay_visible;
/* Control flow graph */
RVecBasicBlockPtr child_nodes;
RVecBasicBlockPtr parent_nodes;
RVecBasicBlockPtr error_handling_child_nodes;
RVecBasicBlockPtr error_handling_parent_nodes;
};
struct BasicBlock **child_nodes;
u32 child_nodes_count;
u32 child_nodes_capacity;
struct BasicBlock **parent_nodes;
u32 parent_nodes_count;
u32 parent_nodes_capacity;
struct BasicBlock **error_handling_child_nodes;
u32 error_handling_child_nodes_count;
u32 error_handling_child_nodes_capacity;
struct BasicBlock **error_handling_parent_nodes;
u32 error_handling_parent_nodes_count;
u32 error_handling_parent_nodes_capacity;
} BasicBlock;
/* Environment for closure variable handling */
struct Environment {
@@ -90,13 +70,11 @@ typedef struct {
} AddressLabels;
/* Decompiled code nesting frame (e.g. for..in loop body scope) */
typedef struct NestedFrame {
typedef struct {
u32 start_address;
u32 end_address;
} NestedFrame;
R_VEC_TYPE(RVecNestedFrame, NestedFrame)
/* Function body decompilation state */
struct DecompiledFunctionBody {
bool is_global;
@@ -121,7 +99,9 @@ struct DecompiledFunctionBody {
ParsedInstruction **throw_anchors; /* Map from address to instruction */
u32 throw_anchors_count;
RVecHBCU32 jump_targets;
u32 *jump_targets; /* Set of addresses */
u32 jump_targets_count;
u32 jump_targets_capacity;
/* Function flags */
bool is_closure;
@@ -134,31 +114,56 @@ struct DecompiledFunctionBody {
Environment **local_items; /* Map from env_register to Environment */
u32 local_items_count;
u32 local_items_capacity;
RVecEnvironmentPtr owned_environments;
Environment **owned_environments; /* Environments allocated for this function */
u32 owned_environments_count;
u32 owned_environments_capacity;
/* Control flow structures */
RVecBasicBlockPtr basic_blocks;
BasicBlock **basic_blocks;
u32 basic_blocks_count;
u32 basic_blocks_capacity;
/* Decompiled-code nesting frames */
RVecNestedFrame nested_frames;
NestedFrame *nested_frames;
u32 nested_frames_count;
u32 nested_frames_capacity;
/* for-in / loop top addresses: a jump here is a `continue`, not a `goto`,
* and the address gets no label (the loop body brace stands in). */
RVecHBCU32 forin_continue_targets;
u32 *forin_continue_targets;
u32 forin_continue_targets_count;
u32 forin_continue_targets_capacity;
/* do-while loops: `do {` opens at `top`, `} while (cond);` replaces the
* back-edge conditional jump at `back_edge`. When an entry guard at
* `guard_pos` (jumping to `exit_addr`) tests the same condition, the loop is
* `promoted` to `while (cond) {` (guard + do/while folded into one header). */
RVecDowhileLoop dowhile_loops;
struct {
u32 top;
u32 back_edge;
u32 guard_pos;
u32 exit_addr;
bool promoted;
/* For a materialized guard (`r=cmp; if(r){do}while(cmp)`), the while
* header renders this condition instead of the guard register. NULL when
* the guard already spells the condition (fused compare-jump). */
void *while_cond;
bool while_cond_invert;
} *dowhile_loops;
u32 dowhile_loops_count;
u32 dowhile_loops_capacity;
/* Infinite loops: a backward unconditional `goto top` is the back-edge of a
* `for (;;) {` opened at `top`; the enclosing if's false path exits at
* `exit`, where a `break;` and the loop close are emitted. */
RVecForeverLoop forever_loops;
struct { u32 top; u32 exit; } *forever_loops;
u32 forever_loops_count;
u32 forever_loops_capacity;
/* Generated code */
RVecHbcTokenString statements;
TokenString *statements;
u32 statements_count;
u32 statements_capacity;
/* Parsed instructions */
ParsedInstructionList instructions;
@@ -181,7 +186,7 @@ typedef struct HermesDecompiler {
bool output_truncated; /* Sticky flag: budget exceeded; stop emitting new content */
bool truncation_marker_emitted; /* Sticky flag: the truncation comment was already written */
HBCDecompOptions options;
RStrBuf output;
StringBuffer output;
} HermesDecompiler;
/* Function declarations */
@@ -191,10 +196,10 @@ Result _hbc_decompiler_cleanup(HermesDecompiler *decompiler);
/* High-level entry points */
Result _hbc_decompile_file(const char *input_file, const char *output_file);
/* Buffer-based APIs used by hermesdec library */
Result _hbc_decompile_all_to_buffer(HBCReader *reader, HBCDecompOptions options, RStrBuf *out);
Result _hbc_decompile_all_with_state(HBC *hbc, HBCDecompOptions options, RStrBuf *out);
Result _hbc_decompile_function_to_buffer(HBCReader *reader, u32 function_id, HBCDecompOptions options, RStrBuf *out);
Result _hbc_decompile_function_with_state(HBC *hbc, u32 function_id, HBCDecompOptions options, RStrBuf *out);
Result _hbc_decompile_all_to_buffer(HBCReader *reader, HBCDecompOptions options, StringBuffer *out);
Result _hbc_decompile_all_with_state(HBC *hbc, HBCDecompOptions options, StringBuffer *out);
Result _hbc_decompile_function_to_buffer(HBCReader *reader, u32 function_id, HBCDecompOptions options, StringBuffer *out);
Result _hbc_decompile_function_with_state(HBC *hbc, u32 function_id, HBCDecompOptions options, StringBuffer *out);
Result _hbc_decompile_function(HermesDecompiler *state, u32 function_id, Environment *parent_environment, int environment_id, bool is_closure, bool is_generator, bool is_async);
/* Transformation passes */
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_LITERALS_H
#define LIBHBC_LITERALS_H
@@ -22,18 +22,18 @@ bool _hbc_is_js_identifier(const char *s);
/* Append a JS object literal reconstructed from object key/value buffers.
* Decodes the Hermes SerializedLiteral buffers into real JS literals.
* Falls back to a compact placeholder comment on failure. */
Result _hbc_format_object_literal(HBCReader *r, u32 key_count, u32 value_count, u32 keys_id, u32 values_id, RStrBuf *out, LiteralsPrettyPolicy policy, bool suppress_comments);
Result _hbc_format_object_literal(HBCReader *r, u32 key_count, u32 value_count, u32 keys_id, u32 values_id, StringBuffer *out, LiteralsPrettyPolicy policy, bool suppress_comments);
/* Append a JS array literal reconstructed from array buffer.
* Decodes the Hermes SerializedLiteral buffers into real JS literals.
* Falls back to a compact placeholder comment on failure. */
Result _hbc_format_array_literal(HBCReader *r, u32 value_count, u32 array_id, RStrBuf *out, LiteralsPrettyPolicy policy, bool suppress_comments);
Result _hbc_format_array_literal(HBCReader *r, u32 value_count, u32 array_id, StringBuffer *out, LiteralsPrettyPolicy policy, bool suppress_comments);
/* Format property access: emits ".name" for identifier-like names, or ["name"] otherwise. */
Result _hbc_format_property_from_string_id(HBCReader *r, u32 string_id, RStrBuf *out);
Result _hbc_format_property_from_string_id(HBCReader *r, u32 string_id, StringBuffer *out);
/* Format a variadic call/construct when only (callee, this, argc) are provided.
* Attempts best-effort representation and falls back to including argc as a comment. */
Result _hbc_format_variadic_call(const ParsedInstruction *insn, RStrBuf *out);
Result _hbc_format_variadic_call(const ParsedInstruction *insn, StringBuffer *out);
#endif /* LIBHBC_LITERALS_H */
+119 -119
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_TOKEN_H
#define LIBHBC_TOKEN_H
@@ -10,97 +10,97 @@
struct HermesDecompiler;
struct Environment;
/* HbcToken types */
/* Token types */
typedef enum {
HBC_TOKEN_TYPE_RAW,
HBC_TOKEN_TYPE_LEFT_HAND_REG,
HBC_TOKEN_TYPE_RIGHT_HAND_REG,
HBC_TOKEN_TYPE_ASSIGNMENT,
HBC_TOKEN_TYPE_LEFT_PARENTHESIS,
HBC_TOKEN_TYPE_RIGHT_PARENTHESIS,
HBC_TOKEN_TYPE_DOT_ACCESSOR,
HBC_TOKEN_TYPE_BIND,
HBC_TOKEN_TYPE_RETURN_DIRECTIVE,
HBC_TOKEN_TYPE_THROW_DIRECTIVE,
HBC_TOKEN_TYPE_FUNCTION_TABLE_INDEX,
HBC_TOKEN_TYPE_JUMP_CONDITION,
HBC_TOKEN_TYPE_JUMP_NOT_CONDITION,
HBC_TOKEN_TYPE_GET_ENVIRONMENT,
HBC_TOKEN_TYPE_LOAD_FROM_ENVIRONMENT,
HBC_TOKEN_TYPE_NEW_ENVIRONMENT,
HBC_TOKEN_TYPE_NEW_INNER_ENVIRONMENT,
HBC_TOKEN_TYPE_SWITCH_IMM,
HBC_TOKEN_TYPE_STORE_TO_ENVIRONMENT,
HBC_TOKEN_TYPE_FOR_IN_LOOP_INIT,
HBC_TOKEN_TYPE_FOR_IN_LOOP_NEXT_ITER,
HBC_TOKEN_TYPE_RESUME_GENERATOR,
HBC_TOKEN_TYPE_SAVE_GENERATOR,
HBC_TOKEN_TYPE_START_GENERATOR,
HBC_TOKEN_TYPE_CATCH_BLOCK_START
} HbcTokenType;
TOKEN_TYPE_RAW,
TOKEN_TYPE_LEFT_HAND_REG,
TOKEN_TYPE_RIGHT_HAND_REG,
TOKEN_TYPE_ASSIGNMENT,
TOKEN_TYPE_LEFT_PARENTHESIS,
TOKEN_TYPE_RIGHT_PARENTHESIS,
TOKEN_TYPE_DOT_ACCESSOR,
TOKEN_TYPE_BIND,
TOKEN_TYPE_RETURN_DIRECTIVE,
TOKEN_TYPE_THROW_DIRECTIVE,
TOKEN_TYPE_FUNCTION_TABLE_INDEX,
TOKEN_TYPE_JUMP_CONDITION,
TOKEN_TYPE_JUMP_NOT_CONDITION,
TOKEN_TYPE_GET_ENVIRONMENT,
TOKEN_TYPE_LOAD_FROM_ENVIRONMENT,
TOKEN_TYPE_NEW_ENVIRONMENT,
TOKEN_TYPE_NEW_INNER_ENVIRONMENT,
TOKEN_TYPE_SWITCH_IMM,
TOKEN_TYPE_STORE_TO_ENVIRONMENT,
TOKEN_TYPE_FOR_IN_LOOP_INIT,
TOKEN_TYPE_FOR_IN_LOOP_NEXT_ITER,
TOKEN_TYPE_RESUME_GENERATOR,
TOKEN_TYPE_SAVE_GENERATOR,
TOKEN_TYPE_START_GENERATOR,
TOKEN_TYPE_CATCH_BLOCK_START
} TokenType;
/* Base token structure */
typedef struct HbcToken {
HbcTokenType type;
struct HbcToken *next;
} HbcToken;
typedef struct Token {
TokenType type;
struct Token *next;
} Token;
/* Raw token with string content */
typedef struct {
HbcToken base;
Token base;
char *text;
} HbcRawToken;
} RawToken;
/* Register token (left-hand side) */
typedef struct {
HbcToken base;
Token base;
int reg_num;
} HbcLeftHandRegToken;
} LeftHandRegToken;
/* Register token (right-hand side) */
typedef struct {
HbcToken base;
Token base;
int reg_num;
} HbcRightHandRegToken;
} RightHandRegToken;
/* Assignment token (=) */
typedef struct {
HbcToken base;
} HbcAssignmentToken;
Token base;
} AssignmentToken;
/* Parenthesis tokens */
typedef struct {
HbcToken base;
} HbcLeftParenthesisToken;
Token base;
} LeftParenthesisToken;
typedef struct {
HbcToken base;
} HbcRightParenthesisToken;
Token base;
} RightParenthesisToken;
/* Dot accessor token (.) */
typedef struct {
HbcToken base;
} HbcDotAccessorToken;
Token base;
} DotAccessorToken;
/* Bind token (.bind ()) */
typedef struct {
HbcToken base;
Token base;
int reg_num;
} HbcBindToken;
} BindToken;
/* Return directive token */
typedef struct {
HbcToken base;
} HbcReturnDirectiveToken;
Token base;
} ReturnDirectiveToken;
/* Throw directive token */
typedef struct {
HbcToken base;
} HbcThrowDirectiveToken;
Token base;
} ThrowDirectiveToken;
/* Function table index token */
typedef struct {
HbcToken base;
Token base;
u32 function_id;
struct HermesDecompiler *state;
int environment_id;
@@ -109,146 +109,146 @@ typedef struct {
bool is_generator;
bool is_async;
struct Environment *parent_environment;
} HbcFunctionTableIndexToken;
} FunctionTableIndexToken;
/* Jump condition tokens */
typedef struct {
HbcToken base;
Token base;
u32 target_address;
} HbcJumpConditionToken;
} JumpConditionToken;
typedef struct {
HbcToken base;
Token base;
u32 target_address;
} HbcJumpNotConditionToken;
} JumpNotConditionToken;
/* Environment tokens */
typedef struct {
HbcToken base;
Token base;
int reg_num;
int nesting_level;
} HbcGetEnvironmentToken;
} GetEnvironmentToken;
typedef struct {
HbcToken base;
Token base;
int reg_num;
int slot_index;
} HbcLoadFromEnvironmentToken;
} LoadFromEnvironmentToken;
typedef struct {
HbcToken base;
Token base;
int reg_num;
} HbcNewEnvironmentToken;
} NewEnvironmentToken;
typedef struct {
HbcToken base;
Token base;
int dest_register;
int parent_register;
int number_of_slots;
} HbcNewInnerEnvironmentToken;
} NewInnerEnvironmentToken;
/* Switch statement token */
typedef struct {
HbcToken base;
Token base;
int value_reg;
u32 jump_table_address;
u32 default_jump_address;
u32 unsigned_min_value;
u32 unsigned_max_value;
} HbcSwitchImmToken;
} SwitchImmToken;
/* Environment store token */
typedef struct {
HbcToken base;
Token base;
int env_register;
int slot_index;
int value_register;
} HbcStoreToEnvironmentToken;
} StoreToEnvironmentToken;
/* For-in loop tokens */
typedef struct {
HbcToken base;
Token base;
int obj_props_register;
int obj_register;
int iter_index_register;
int iter_size_register;
} HbcForInLoopInitToken;
} ForInLoopInitToken;
typedef struct {
HbcToken base;
Token base;
int next_value_register;
int obj_props_register;
int obj_register;
int iter_index_register;
int iter_size_register;
} HbcForInLoopNextIterToken;
} ForInLoopNextIterToken;
/* Generator tokens */
typedef struct {
HbcToken base;
Token base;
int result_out_reg;
int return_bool_out_reg;
} HbcResumeGeneratorToken;
} ResumeGeneratorToken;
typedef struct {
HbcToken base;
Token base;
u32 address;
} HbcSaveGeneratorToken;
} SaveGeneratorToken;
typedef struct {
HbcToken base;
} HbcStartGeneratorToken;
Token base;
} StartGeneratorToken;
/* Try-catch token */
typedef struct {
HbcToken base;
Token base;
int arg_register;
} HbcCatchBlockStartToken;
} CatchBlockStartToken;
/* HbcToken string (list of tokens) */
/* Token string (list of tokens) */
typedef struct {
HbcToken *head;
HbcToken *tail;
Token *head;
Token *tail;
ParsedInstruction *assembly;
} HbcTokenString;
} TokenString;
/* Function declarations */
Result _hbc_token_string_init(HbcTokenString *token_string, ParsedInstruction *instruction);
void _hbc_token_string_cleanup(HbcTokenString *token_string);
Result _hbc_token_string_init(TokenString *token_string, ParsedInstruction *instruction);
void _hbc_token_string_cleanup(TokenString *token_string);
/* HbcToken creation functions */
HbcToken *hbc_token_new_raw(const char *text);
HbcToken *hbc_token_new_left_hand_reg(int reg_num);
HbcToken *hbc_token_new_right_hand_reg(int reg_num);
HbcToken *hbc_token_new_assignment(void);
HbcToken *hbc_token_new_left_parenthesis(void);
HbcToken *hbc_token_new_right_parenthesis(void);
HbcToken *hbc_token_new_dot_accessor(void);
HbcToken *hbc_token_new_bind(int reg_num);
HbcToken *hbc_token_new_return_directive(void);
HbcToken *hbc_token_new_throw_directive(void);
HbcToken *hbc_token_new_function_table_index(u32 function_id, struct HermesDecompiler *state);
HbcToken *hbc_token_new_jump_condition(u32 target_address);
HbcToken *hbc_token_new_jump_not_condition(u32 target_address);
/* Token creation functions */
Token *create_raw_token(const char *text);
Token *create_left_hand_reg_token(int reg_num);
Token *create_right_hand_reg_token(int reg_num);
Token *create_assignment_token(void);
Token *create_left_parenthesis_token(void);
Token *create_right_parenthesis_token(void);
Token *create_dot_accessor_token(void);
Token *create_bind_token(int reg_num);
Token *create_return_directive_token(void);
Token *create_throw_directive_token(void);
Token *create_function_table_index_token(u32 function_id, struct HermesDecompiler *state);
Token *create_jump_condition_token(u32 target_address);
Token *create_jump_not_condition_token(u32 target_address);
/* Advanced token creation */
HbcToken *hbc_token_new_get_environment(int reg_num, int nesting_level);
HbcToken *hbc_token_new_load_from_environment(int reg_num, int slot_index);
HbcToken *hbc_token_new_new_environment(int reg_num);
HbcToken *hbc_token_new_new_inner_environment(int dest_register, int parent_register, int number_of_slots);
HbcToken *hbc_token_new_switch_imm(int value_reg, u32 jump_table_address, u32 default_jump_address, u32 unsigned_min_value, u32 unsigned_max_value);
HbcToken *hbc_token_new_store_to_environment(int env_register, int slot_index, int value_register);
HbcToken *hbc_token_new_for_in_loop_init(int obj_props_register, int obj_register, int iter_index_register, int iter_size_register);
HbcToken *hbc_token_new_for_in_loop_next_iter(int next_value_register, int obj_props_register, int obj_register, int iter_index_register, int iter_size_register);
HbcToken *hbc_token_new_resume_generator(int result_out_reg, int return_bool_out_reg);
HbcToken *hbc_token_new_save_generator(u32 address);
HbcToken *hbc_token_new_start_generator(void);
HbcToken *hbc_token_new_catch_block_start(int arg_register);
Token *create_get_environment_token(int reg_num, int nesting_level);
Token *create_load_from_environment_token(int reg_num, int slot_index);
Token *create_new_environment_token(int reg_num);
Token *create_new_inner_environment_token(int dest_register, int parent_register, int number_of_slots);
Token *create_switch_imm_token(int value_reg, u32 jump_table_address, u32 default_jump_address, u32 unsigned_min_value, u32 unsigned_max_value);
Token *create_store_to_environment_token(int env_register, int slot_index, int value_register);
Token *create_for_in_loop_init_token(int obj_props_register, int obj_register, int iter_index_register, int iter_size_register);
Token *create_for_in_loop_next_iter_token(int next_value_register, int obj_props_register, int obj_register, int iter_index_register, int iter_size_register);
Token *create_resume_generator_token(int result_out_reg, int return_bool_out_reg);
Token *create_save_generator_token(u32 address);
Token *create_start_generator_token(void);
Token *create_catch_block_start_token(int arg_register);
/* HbcToken manipulation */
Result _hbc_token_string_add_token(HbcTokenString *token_string, HbcToken *token);
void _hbc_token_free(HbcToken *token);
Result _hbc_token_to_string(HbcToken *token, RStrBuf *buffer);
Result _hbc_token_string_to_string(HbcTokenString *token_string, RStrBuf *buffer);
/* Token manipulation */
Result _hbc_token_string_add_token(TokenString *token_string, Token *token);
void _hbc_token_free(Token *token);
Result _hbc_token_to_string(Token *token, StringBuffer *buffer);
Result _hbc_token_string_to_string(TokenString *token_string, StringBuffer *buffer);
#endif /* LIBHBC_TOKEN_H */
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_TRANSLATOR_H
#define LIBHBC_TRANSLATOR_H
@@ -7,7 +7,7 @@
#include <hbc/bytecode.h>
#include "token.h"
/* Translate a single parsed instruction into a HbcTokenString */
Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn, HbcTokenString *out);
/* Translate a single parsed instruction into a TokenString */
Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn, TokenString *out);
#endif /* LIBHBC_TRANSLATOR_H */
+2 -2
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_DISASSEMBLER_H
#define LIBHBC_DISASSEMBLER_H
@@ -13,7 +13,7 @@
/* Disassembler state */
typedef struct {
HBCReader *reader; /* The HBC reader with parsed data */
RStrBuf output; /* The output buffer */
StringBuffer output; /* The output buffer */
HBCDisOptions options; /* Disassembly options */
u32 current_function_id; /* Function currently being disassembled */
} Disassembler;
+3 -35
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_API_H
#define LIBHBC_API_H
@@ -57,33 +57,14 @@ 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
@@ -169,8 +150,8 @@ typedef struct {
bool inline_closures; /* Inline closure definitions (default: true) */
int inline_threshold; /* Max closure bytecode size to inline (0 = no limit, -1 = no inline) */
int inline_max_depth; /* Max nested inline depth (0 = no limit). Bounds the
* recursive closure expansion that otherwise inlines a
* whole module graph into one function. */
* recursive closure expansion that otherwise inlines a
* whole module graph into one function. */
/* Early-stop budgets (0 = unlimited). max_ast_statements caps pass2 AST
* growth per function; max_output_bytes caps total emitted output. */
int max_ast_statements;
@@ -241,12 +222,6 @@ HBC_API Result hbc_open(const char *path, HBC **out);
*/
HBC_API Result hbc_open_from_memory(const u8 *data, size_t size, HBC **out);
/**
* Open an HBC file from an existing radare2 RBuffer.
* Creates an HBC with its own RBuffer cursor/reference.
*/
HBC_API Result hbc_open_from_rbuffer(RBuffer *buf, HBC **out);
/**
* Close an HBC and free all resources.
* After this call, all pointers returned from the state are invalid.
@@ -273,13 +248,6 @@ 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
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_ENCODER_H
#define LIBHBC_ENCODER_H
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_LITERALS_PUBLIC_H
#define LIBHBC_LITERALS_PUBLIC_H
@@ -40,13 +40,13 @@ 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 array data or object values (0 if unknown) */
u32 paddr; /* absolute file offset of the literal data in the SLP pool (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;
} HBCLiteralEntry;
typedef bool(*HBCLiteralKeyCallback)(const char *key, void *user);
typedef bool (*HBCLiteralKeyCallback)(const char *key, void *user);
/* Format a literal directly from raw parameters. Does not touch the cache.
* Returned string is heap-allocated; caller frees with free (). */
+1 -1
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_HERMES_OPCODES_H
#define LIBHBC_HERMES_OPCODES_H
+2 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_FILE_PARSER_H
#define LIBHBC_FILE_PARSER_H
@@ -213,8 +213,7 @@ struct BytecodeModule {
/* Main HBC reader structure */
struct HBCReader {
/* File data */
RBuffer *file_buffer;
u8 *file_bytes_cache;
BufferReader file_buffer;
/* Parsed header and tables */
HBCHeader header;
+11 -67
View File
@@ -1,7 +1,7 @@
project(
'r2hermes',
'c',
version: '1.6.2',
version: '1.4.2',
license: 'MIT',
default_options: [
'c_std=c11',
@@ -39,67 +39,11 @@ configure_file(
# Include directories
include_dirs = include_directories('include')
# radare2 headers are needed by libhbc because it uses r_vec.h internally.
# Prefer pkg-config/CMake, but fall back to the flags reported by r2 itself
# for environments such as Windows CI where pkg-config may be unavailable.
r2_bin = find_program('r2', required: false)
r2_cflags = []
r2_ldflags = []
r2_libs = []
if r2_bin.found()
r2_cflags_cmd = run_command(r2_bin, '-H', 'R2_CFLAGS', check: false)
if r2_cflags_cmd.returncode() == 0
r2_cflags = r2_cflags_cmd.stdout().strip().split()
endif
r2_ldflags_cmd = run_command(r2_bin, '-H', 'R2_LDFLAGS', check: false)
if r2_ldflags_cmd.returncode() == 0
r2_ldflags = r2_ldflags_cmd.stdout().strip().split()
endif
r2_libs_cmd = run_command(r2_bin, '-H', 'R2_LIBS', check: false)
if r2_libs_cmd.returncode() == 0
r2_libs = r2_libs_cmd.stdout().strip().split()
endif
endif
if r2_cflags.length() > 0
add_project_arguments(r2_cflags, language: 'c')
endif
r2_core = dependency('r_core', required: false)
r2_util = dependency('r_util', required: false)
r2_link_args = r2_ldflags + r2_libs
r2_util_link_args = r2_ldflags
if r2_libs.contains('-lr_util')
r2_util_link_args += ['-lr_util']
else
r2_util_link_args += r2_libs
endif
if not r2_core.found() and r2_cflags.length() > 0 and r2_link_args.length() > 0
r2_core = declare_dependency(
compile_args: r2_cflags,
link_args: r2_link_args,
)
message('radare2 dependency resolved from r2 -H flags')
endif
if not r2_util.found() and r2_cflags.length() > 0 and r2_util_link_args.length() > 0
r2_util = declare_dependency(
compile_args: r2_cflags,
link_args: r2_util_link_args,
)
message('radare2 util dependency resolved from r2 -H flags')
endif
libhbc_compile_deps = []
libhbc_link_deps = []
if r2_util.found()
libhbc_link_deps += r2_util
if r2_cflags.length() == 0
libhbc_compile_deps += r2_util
endif
endif
# Library source files
lib_utils_src = []
lib_utils_src = files(
'src/lib/utils/string_buffer.c',
'src/lib/utils/buffer_reader.c',
)
lib_parsers_src = files(
'src/lib/parsers/hbc_file_parser.c',
@@ -135,7 +79,6 @@ libhbc = static_library(
lib_src,
include_directories: include_dirs,
c_args: ['-pedantic'],
dependencies: libhbc_compile_deps,
install: true,
)
@@ -143,7 +86,6 @@ libhbc = static_library(
libhbc_dep = declare_dependency(
link_with: libhbc,
include_directories: include_dirs,
dependencies: libhbc_link_deps,
)
# Install library headers
@@ -160,29 +102,31 @@ executable(
link_with: libhbc,
include_directories: include_dirs,
c_args: ['-pedantic'],
dependencies: libhbc_link_deps,
install: true,
)
# radare2 plugins
r2_core = dependency('r_core', required: false)
if r2_core.found()
# Get radare2 plugin directory
r2_plugdir_opt = get_option('r2_plugdir')
r2_prefix_opt = get_option('r2_prefix')
r2_found = false
if r2_plugdir_opt == '' or r2_prefix_opt == ''
r2_found = r2_bin.found()
r2_found = run_command('which', 'r2', check: false).returncode() == 0
endif
if r2_plugdir_opt != ''
r2_plugdir = r2_plugdir_opt
elif r2_found
r2_plugdir = run_command(r2_bin, '-H', 'R2_USER_PLUGINS', check: false).stdout().strip()
r2_plugdir = run_command('r2', '-H', 'R2_USER_PLUGINS', check: false).stdout().strip()
else
r2_plugdir = r2_plugdir_opt
endif
if r2_prefix_opt != ''
r2_prefix = r2_prefix_opt
elif r2_found
r2_prefix = run_command(r2_bin, '-H', 'R2_PREFIX', check: false).stdout().strip()
r2_prefix = run_command('r2', '-H', 'R2_PREFIX', check: false).stdout().strip()
else
r2_prefix = r2_prefix_opt
endif
+1 -1
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifdef R2_CORE_H
extern RCorePlugin r_core_plugin_r2hermes;
+3 -24
View File
@@ -1,26 +1,5 @@
HBC_CORE_WD=$(LIBR)/xps/p/r2hermes
HBC_CORE_R2_CFLAGS=-I$(LIBR) -I$(LIBR)/include -I$(LIBR)/../shlr -I$(LIBR)/../subprojects/sdb/include
CFLAGS+=-I$(HBC_CORE_WD)/include $(HBC_CORE_R2_CFLAGS)
HBC_CORE_SRC=\
$(HBC_CORE_WD)/src/lib/parsers/hbc_file_parser.c \
$(HBC_CORE_WD)/src/lib/parsers/hbc_bytecode_parser.c \
$(HBC_CORE_WD)/src/lib/decompilation/translator.c \
$(HBC_CORE_WD)/src/lib/decompilation/token.c \
$(HBC_CORE_WD)/src/lib/decompilation/literals.c \
$(HBC_CORE_WD)/src/lib/decompilation/decompiler.c \
$(HBC_CORE_WD)/src/lib/opcodes/isa.c \
$(HBC_CORE_WD)/src/lib/opcodes/encoder.c \
$(HBC_CORE_WD)/src/lib/opcodes/decoder.c \
$(HBC_CORE_WD)/src/lib/hbc.c \
$(HBC_CORE_WD)/src/lib/literals_api.c \
$(HBC_CORE_WD)/src/lib/r2.c
HBC_CORE_LIB_OBJS=$(patsubst $(HBC_CORE_WD)/src/lib/%.c,$(HBC_CORE_WD)/build/r2plugin/%.o,$(HBC_CORE_SRC))
HBC_CORE_HEADERS=$(shell find $(HBC_CORE_WD)/include -name '*.h')
CFLAGS+=-I$(HBC_CORE_WD)/include
HBC_CORE_OBJ=$(HBC_CORE_WD)/src/r2/core_hbc_one.o
EXTERNAL_STATIC_OBJS+=$(HBC_CORE_LIB_OBJS) $(HBC_CORE_OBJ)
$(HBC_CORE_LIB_OBJS) $(HBC_CORE_OBJ): $(HBC_CORE_HEADERS)
$(HBC_CORE_WD)/build/r2plugin/%.o: $(HBC_CORE_WD)/src/lib/%.c
@mkdir -p $(dir $@)
$(CC) -c $(CFLAGS) -o $@ $<
LDFLAGS+=$(HBC_CORE_WD)/build/libhbc.a
EXTERNAL_STATIC_OBJS+=$(HBC_CORE_OBJ)
+2
View File
@@ -8,6 +8,8 @@ core_plugins += ['r2hermes']
r_core_additional_inc += include_directories(join_paths('..', 'include'))
r_core_additional_sources += files(
'../src/lib/utils/string_buffer.c',
'../src/lib/utils/buffer_reader.c',
'../src/lib/parsers/hbc_file_parser.c',
'../src/lib/parsers/hbc_bytecode_parser.c',
'../src/lib/decompilation/translator.c',
+1 -1
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifdef R2_CORE_H
#include "core/deps.h"
File diff suppressed because it is too large. Load diff
+50 -50
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/decompilation/literals.h>
#include <ctype.h>
@@ -21,38 +21,38 @@ bool _hbc_is_js_identifier(const char *s) {
return true;
}
static Result append_quoted(RStrBuf *out, const char *s) {
RETURN_IF_ERROR (r_strbuf_append (out, "\""));
static Result append_quoted(StringBuffer *out, const char *s) {
RETURN_IF_ERROR (_hbc_sb_append (out, "\""));
for (const char *p = s; p && *p; p++) {
unsigned char c = (unsigned char)*p;
if (c == '"' || c == '\\') {
RETURN_IF_ERROR (r_strbuf_appendf (out, "%c", '\\'));
RETURN_IF_ERROR (r_strbuf_appendf (out, "%c", (char)c));
RETURN_IF_ERROR (_hbc_sb_append_char (out, '\\'));
RETURN_IF_ERROR (_hbc_sb_append_char (out, (char)c));
} else if (c < 0x20) {
RETURN_IF_ERROR (r_strbuf_appendf (out, "\\x%02x", c));
RETURN_IF_ERROR (_hbc_sb_appendf (out, "\\x%02x", c));
} else {
RETURN_IF_ERROR (r_strbuf_appendf (out, "%c", (char)c));
RETURN_IF_ERROR (_hbc_sb_append_char (out, (char)c));
}
}
return HBC_TO_RESULT (r_strbuf_append (out, "\""));
return _hbc_sb_append (out, "\"");
}
Result _hbc_format_property_from_string_id(HBCReader *r, u32 string_id, RStrBuf *out) {
Result _hbc_format_property_from_string_id(HBCReader *r, u32 string_id, StringBuffer *out) {
const char *s = NULL;
if (r && r->strings && string_id < r->header.stringCount) {
s = r->strings[string_id];
}
if (_hbc_is_js_identifier (s)) {
RETURN_IF_ERROR (r_strbuf_append (out, "."));
return HBC_TO_RESULT (r_strbuf_append (out, s));
RETURN_IF_ERROR (_hbc_sb_append (out, "."));
return _hbc_sb_append (out, s);
}
RETURN_IF_ERROR (r_strbuf_append (out, "["));
RETURN_IF_ERROR (_hbc_sb_append (out, "["));
if (s) {
RETURN_IF_ERROR (append_quoted (out, s));
} else {
RETURN_IF_ERROR (r_strbuf_append (out, "\"\""));
RETURN_IF_ERROR (_hbc_sb_append (out, "\"\""));
}
RETURN_IF_ERROR (r_strbuf_append (out, "]"));
RETURN_IF_ERROR (_hbc_sb_append (out, "]"));
return SUCCESS_RESULT ();
}
@@ -194,19 +194,19 @@ static bool slp_parse_values(const u8 *base, size_t size, u32 offset, u32 num_it
return true;
}
static Result slp_append_value_js(HBCReader *r, const SLPValue *v, bool for_key, RStrBuf *out) {
static Result slp_append_value_js(HBCReader *r, const SLPValue *v, bool for_key, StringBuffer *out) {
switch (v->tag) {
case SLP_NullTag: return HBC_TO_RESULT (r_strbuf_append (out, "null"));
case SLP_TrueTag: return HBC_TO_RESULT (r_strbuf_append (out, "true"));
case SLP_FalseTag: return HBC_TO_RESULT (r_strbuf_append (out, "false"));
case SLP_NullTag: return _hbc_sb_append (out, "null");
case SLP_TrueTag: return _hbc_sb_append (out, "true");
case SLP_FalseTag: return _hbc_sb_append (out, "false");
case SLP_IntegerTag:
{
return HBC_TO_RESULT (r_strbuf_appendf (out, "%u", (unsigned)v->v.intv));
return _hbc_sb_appendf (out, "%u", (unsigned)v->v.intv);
}
case SLP_NumberTag:
{
/* Use %.17g to preserve precision while avoiding trailing noise */
return HBC_TO_RESULT (r_strbuf_appendf (out, "%.17g", v->v.num));
return _hbc_sb_appendf (out, "%.17g", v->v.num);
}
case SLP_LongStringTag:
case SLP_ShortStringTag:
@@ -214,26 +214,26 @@ static Result slp_append_value_js(HBCReader *r, const SLPValue *v, bool for_key,
{
const char *s = (r && r->strings && v->v.str_id < r->header.stringCount)? r->strings[v->v.str_id]: NULL;
if (for_key && s && _hbc_is_js_identifier (s)) {
return HBC_TO_RESULT (r_strbuf_append (out, s));
return _hbc_sb_append (out, s);
}
if (s) {
return append_quoted (out, s);
}
return HBC_TO_RESULT (r_strbuf_append (out, "\"\""));
return _hbc_sb_append (out, "\"\"");
}
default:
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Unknown SLP tag");
}
}
Result _hbc_format_object_literal(HBCReader *r, u32 key_count, u32 value_count, u32 keys_id, u32 values_id, RStrBuf *out, LiteralsPrettyPolicy policy, bool suppress_comments) {
Result _hbc_format_object_literal(HBCReader *r, u32 key_count, u32 value_count, u32 keys_id, u32 values_id, StringBuffer *out, LiteralsPrettyPolicy policy, bool suppress_comments) {
if (r && r->header.version >= 97 && r->object_shapes && r->object_shape_count > 0) {
u32 value_offset = value_count;
if (key_count >= r->object_shape_count || !r->object_keys || !r->object_values) {
if (suppress_comments) {
return HBC_TO_RESULT (r_strbuf_append (out, "}"));
return _hbc_sb_append (out, "}");
}
return HBC_TO_RESULT (r_strbuf_appendf (out, " /*shape:%u vals:%u*/ }", key_count, value_count));
return _hbc_sb_appendf (out, " /*shape:%u vals:%u*/ }", key_count, value_count);
}
const ShapeTableEntry *shape = &r->object_shapes[key_count];
key_count = shape->prop_count;
@@ -244,13 +244,13 @@ Result _hbc_format_object_literal(HBCReader *r, u32 key_count, u32 value_count,
/* Apply policy */
bool pretty = (policy == LITERALS_PRETTY_ALWAYS) || (policy == LITERALS_PRETTY_AUTO && (key_count > 0));
bool multiline = pretty && (key_count > 0);
RETURN_IF_ERROR (r_strbuf_append (out, multiline? "{\n": "{"));
RETURN_IF_ERROR (_hbc_sb_append (out, multiline? "{\n": "{"));
if (!r || key_count == 0 || value_count == 0 || !r->object_keys || !r->object_values) {
/* Fallback placeholder */
if (suppress_comments) {
return HBC_TO_RESULT (r_strbuf_append (out, "}"));
return _hbc_sb_append (out, "}");
}
return HBC_TO_RESULT (r_strbuf_appendf (out, " /*k:%u id:%u vals:%u*/ }", key_count, keys_id, values_id));
return _hbc_sb_appendf (out, " /*k:%u id:%u vals:%u*/ }", key_count, keys_id, values_id);
}
u32 n = key_count < value_count? key_count: value_count;
SLPValue *key_vals = (SLPValue *)calloc (n, sizeof (SLPValue));
@@ -272,46 +272,46 @@ Result _hbc_format_object_literal(HBCReader *r, u32 key_count, u32 value_count,
free (key_vals);
free (val_vals);
if (suppress_comments) {
return HBC_TO_RESULT (r_strbuf_append (out, "}"));
return _hbc_sb_append (out, "}");
}
return HBC_TO_RESULT (r_strbuf_appendf (out, " /*k:%u id:%u vals:%u*/ }", key_count, keys_id, values_id));
return _hbc_sb_appendf (out, " /*k:%u id:%u vals:%u*/ }", key_count, keys_id, values_id);
}
for (u32 i = 0; i < n; i++) {
if (multiline) {
RETURN_IF_ERROR (r_strbuf_append (out, " "));
RETURN_IF_ERROR (_hbc_sb_append (out, " "));
}
if (!multiline && i) {
RETURN_IF_ERROR (r_strbuf_append (out, ", "));
RETURN_IF_ERROR (_hbc_sb_append (out, ", "));
}
/* Key: allow identifiers and numeric keys unquoted; otherwise quote. */
RETURN_IF_ERROR (slp_append_value_js (r, &key_vals[i], true /*for_key*/, out));
RETURN_IF_ERROR (r_strbuf_append (out, ": "));
RETURN_IF_ERROR (_hbc_sb_append (out, ": "));
/* Value: always a valid JS literal */
RETURN_IF_ERROR (slp_append_value_js (r, &val_vals[i], false, out));
if (multiline) {
RETURN_IF_ERROR (r_strbuf_append (out, i + 1 < n? ",\n": "\n"));
RETURN_IF_ERROR (_hbc_sb_append (out, i + 1 < n? ",\n": "\n"));
}
}
free (key_vals);
free (val_vals);
RETURN_IF_ERROR (r_strbuf_append (out, multiline? "}": "}"));
RETURN_IF_ERROR (_hbc_sb_append (out, multiline? "}": "}"));
return SUCCESS_RESULT ();
}
Result _hbc_format_array_literal(HBCReader *r, u32 value_count, u32 array_id, RStrBuf *out, LiteralsPrettyPolicy policy, bool suppress_comments) {
Result _hbc_format_array_literal(HBCReader *r, u32 value_count, u32 array_id, StringBuffer *out, LiteralsPrettyPolicy policy, bool suppress_comments) {
/* Apply policy. AUTO renders arrays inline (`[1, 2, 3]`): these are data
*(e.g. module dependency lists) often hundreds long, and one-element-per-
* (e.g. module dependency lists) often hundreds long, and one-element-per-
* line dominates the output while its embedded newlines ignore the statement
* indent. Only ALWAYS expands them vertically. */
bool multiline = (policy == LITERALS_PRETTY_ALWAYS) && (value_count > 0);
RETURN_IF_ERROR (r_strbuf_append (out, multiline? "[\n": "["));
RETURN_IF_ERROR (_hbc_sb_append (out, multiline? "[\n": "["));
if (!r || !r->arrays || value_count == 0) {
if (suppress_comments) {
return HBC_TO_RESULT (r_strbuf_append (out, "]"));
return _hbc_sb_append (out, "]");
}
return HBC_TO_RESULT (r_strbuf_appendf (out, " /*n:%u id:%u*/ ]", value_count, array_id));
return _hbc_sb_appendf (out, " /*n:%u id:%u*/ ]", value_count, array_id);
}
SLPValue *vals = (SLPValue *)calloc (value_count, sizeof (SLPValue));
if (!vals) {
@@ -321,39 +321,39 @@ Result _hbc_format_array_literal(HBCReader *r, u32 value_count, u32 array_id, RS
if (!ok) {
free (vals);
if (suppress_comments) {
return HBC_TO_RESULT (r_strbuf_append (out, "]"));
return _hbc_sb_append (out, "]");
}
return HBC_TO_RESULT (r_strbuf_appendf (out, " /*n:%u id:%u*/ ]", value_count, array_id));
return _hbc_sb_appendf (out, " /*n:%u id:%u*/ ]", value_count, array_id);
}
for (u32 i = 0; i < value_count; i++) {
if (multiline) {
RETURN_IF_ERROR (r_strbuf_append (out, " "));
RETURN_IF_ERROR (_hbc_sb_append (out, " "));
}
if (!multiline && i) {
RETURN_IF_ERROR (r_strbuf_append (out, ", "));
RETURN_IF_ERROR (_hbc_sb_append (out, ", "));
}
RETURN_IF_ERROR (slp_append_value_js (r, &vals[i], false, out));
if (multiline) {
RETURN_IF_ERROR (r_strbuf_append (out, i + 1 < value_count? ",\n": "\n"));
RETURN_IF_ERROR (_hbc_sb_append (out, i + 1 < value_count? ",\n": "\n"));
}
}
free (vals);
RETURN_IF_ERROR (r_strbuf_append (out, "]"));
RETURN_IF_ERROR (_hbc_sb_append (out, "]"));
return SUCCESS_RESULT ();
}
Result _hbc_format_variadic_call(const ParsedInstruction *insn, RStrBuf *out) {
Result _hbc_format_variadic_call(const ParsedInstruction *insn, StringBuffer *out) {
/* Known operands: arg1=callee, arg2=this, arg3=argc or imm32 argc. */
u32 argc = insn->arg3;
if (argc > 0) {
/* Heuristic: arguments follow 'this' in successive registers */
u32 base = insn->arg2;
for (u32 i = 1; i <= argc; i++) {
RETURN_IF_ERROR (r_strbuf_append (out, ", "));
RETURN_IF_ERROR (r_strbuf_appendf (out, "r%u", base + i));
RETURN_IF_ERROR (_hbc_sb_append (out, ", "));
RETURN_IF_ERROR (_hbc_sb_appendf (out, "r%u", base + i));
}
/* Also annotate argc */
RETURN_IF_ERROR (r_strbuf_appendf (out, " /*argc:%u*/", argc));
RETURN_IF_ERROR (_hbc_sb_appendf (out, " /*argc:%u*/", argc));
} else {
/* No args beyond 'this' */
}
+166 -176
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/decompilation/token.h>
#include <hbc/decompilation/decompiler.h>
@@ -6,8 +6,8 @@
#include <ctype.h>
static HbcToken *alloc_token(HbcTokenType type, size_t extra) {
HbcToken *t = (HbcToken *)malloc ((sizeof (HbcToken)) + extra);
static Token *alloc_token(TokenType type, size_t extra) {
Token *t = (Token *)malloc ((sizeof (Token)) + extra);
if (!t) {
return NULL;
}
@@ -16,7 +16,7 @@ static HbcToken *alloc_token(HbcTokenType type, size_t extra) {
return t;
}
Result _hbc_token_string_init(HbcTokenString *ts, ParsedInstruction *insn) {
Result _hbc_token_string_init(TokenString *ts, ParsedInstruction *insn) {
if (!ts) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "_hbc_token_string_init: ts NULL");
}
@@ -25,13 +25,13 @@ Result _hbc_token_string_init(HbcTokenString *ts, ParsedInstruction *insn) {
return SUCCESS_RESULT ();
}
void _hbc_token_string_cleanup(HbcTokenString *ts) {
void _hbc_token_string_cleanup(TokenString *ts) {
if (!ts) {
return;
}
HbcToken *cur = ts->head;
Token *cur = ts->head;
while (cur) {
HbcToken *nxt = cur->next;
Token *nxt = cur->next;
_hbc_token_free (cur);
cur = nxt;
}
@@ -39,7 +39,7 @@ void _hbc_token_string_cleanup(HbcTokenString *ts) {
ts->assembly = NULL;
}
Result _hbc_token_string_add_token(HbcTokenString *ts, HbcToken *t) {
Result _hbc_token_string_add_token(TokenString *ts, Token *t) {
if (!ts || !t) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "_hbc_token_string_add_token: NULL");
}
@@ -52,11 +52,11 @@ Result _hbc_token_string_add_token(HbcTokenString *ts, HbcToken *t) {
return SUCCESS_RESULT ();
}
HbcToken *hbc_token_new_raw(const char *text) {
Token *create_raw_token(const char *text) {
if (!text) {
return NULL;
}
HbcRawToken *rt = (HbcRawToken *)alloc_token (HBC_TOKEN_TYPE_RAW, sizeof (HbcRawToken) - sizeof (HbcToken));
RawToken *rt = (RawToken *)alloc_token (TOKEN_TYPE_RAW, sizeof (RawToken) - sizeof (Token));
if (!rt) {
return NULL;
}
@@ -67,59 +67,59 @@ HbcToken *hbc_token_new_raw(const char *text) {
return NULL;
}
memcpy (rt->text, text, len + 1);
return (HbcToken *)rt;
return (Token *)rt;
}
HbcToken *hbc_token_new_left_hand_reg(int reg_num) {
HbcLeftHandRegToken *t = (HbcLeftHandRegToken *)alloc_token (HBC_TOKEN_TYPE_LEFT_HAND_REG, sizeof (HbcLeftHandRegToken) - sizeof (HbcToken));
Token *create_left_hand_reg_token(int reg_num) {
LeftHandRegToken *t = (LeftHandRegToken *)alloc_token (TOKEN_TYPE_LEFT_HAND_REG, sizeof (LeftHandRegToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_right_hand_reg(int reg_num) {
HbcRightHandRegToken *t = (HbcRightHandRegToken *)alloc_token (HBC_TOKEN_TYPE_RIGHT_HAND_REG, sizeof (HbcRightHandRegToken) - sizeof (HbcToken));
Token *create_right_hand_reg_token(int reg_num) {
RightHandRegToken *t = (RightHandRegToken *)alloc_token (TOKEN_TYPE_RIGHT_HAND_REG, sizeof (RightHandRegToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_assignment(void) {
return alloc_token (HBC_TOKEN_TYPE_ASSIGNMENT, 0);
Token *create_assignment_token(void) {
return alloc_token (TOKEN_TYPE_ASSIGNMENT, 0);
}
HbcToken *hbc_token_new_left_parenthesis(void) {
return alloc_token (HBC_TOKEN_TYPE_LEFT_PARENTHESIS, 0);
Token *create_left_parenthesis_token(void) {
return alloc_token (TOKEN_TYPE_LEFT_PARENTHESIS, 0);
}
HbcToken *hbc_token_new_right_parenthesis(void) {
return alloc_token (HBC_TOKEN_TYPE_RIGHT_PARENTHESIS, 0);
Token *create_right_parenthesis_token(void) {
return alloc_token (TOKEN_TYPE_RIGHT_PARENTHESIS, 0);
}
HbcToken *hbc_token_new_dot_accessor(void) {
return alloc_token (HBC_TOKEN_TYPE_DOT_ACCESSOR, 0);
Token *create_dot_accessor_token(void) {
return alloc_token (TOKEN_TYPE_DOT_ACCESSOR, 0);
}
HbcToken *hbc_token_new_bind(int reg_num) {
HbcBindToken *t = (HbcBindToken *)alloc_token (HBC_TOKEN_TYPE_BIND, sizeof (HbcBindToken) - sizeof (HbcToken));
Token *create_bind_token(int reg_num) {
BindToken *t = (BindToken *)alloc_token (TOKEN_TYPE_BIND, sizeof (BindToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_return_directive(void) {
return alloc_token (HBC_TOKEN_TYPE_RETURN_DIRECTIVE, 0);
Token *create_return_directive_token(void) {
return alloc_token (TOKEN_TYPE_RETURN_DIRECTIVE, 0);
}
HbcToken *hbc_token_new_throw_directive(void) {
return alloc_token (HBC_TOKEN_TYPE_THROW_DIRECTIVE, 0);
Token *create_throw_directive_token(void) {
return alloc_token (TOKEN_TYPE_THROW_DIRECTIVE, 0);
}
HbcToken *hbc_token_new_function_table_index(u32 function_id, struct HermesDecompiler *state) {
HbcFunctionTableIndexToken *t = (HbcFunctionTableIndexToken *)alloc_token (HBC_TOKEN_TYPE_FUNCTION_TABLE_INDEX, sizeof (HbcFunctionTableIndexToken) - sizeof (HbcToken));
Token *create_function_table_index_token(u32 function_id, struct HermesDecompiler *state) {
FunctionTableIndexToken *t = (FunctionTableIndexToken *)alloc_token (TOKEN_TYPE_FUNCTION_TABLE_INDEX, sizeof (FunctionTableIndexToken) - sizeof (Token));
if (!t) {
return NULL;
}
@@ -131,69 +131,69 @@ HbcToken *hbc_token_new_function_table_index(u32 function_id, struct HermesDecom
t->is_generator = false;
t->is_async = false;
t->parent_environment = NULL;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_jump_condition(u32 target_address) {
HbcJumpConditionToken *t = (HbcJumpConditionToken *)alloc_token (HBC_TOKEN_TYPE_JUMP_CONDITION, sizeof (HbcJumpConditionToken) - sizeof (HbcToken));
Token *create_jump_condition_token(u32 target_address) {
JumpConditionToken *t = (JumpConditionToken *)alloc_token (TOKEN_TYPE_JUMP_CONDITION, sizeof (JumpConditionToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->target_address = target_address;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_jump_not_condition(u32 target_address) {
HbcJumpNotConditionToken *t = (HbcJumpNotConditionToken *)alloc_token (HBC_TOKEN_TYPE_JUMP_NOT_CONDITION, sizeof (HbcJumpNotConditionToken) - sizeof (HbcToken));
Token *create_jump_not_condition_token(u32 target_address) {
JumpNotConditionToken *t = (JumpNotConditionToken *)alloc_token (TOKEN_TYPE_JUMP_NOT_CONDITION, sizeof (JumpNotConditionToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->target_address = target_address;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_get_environment(int reg_num, int nesting_level) {
HbcGetEnvironmentToken *t = (HbcGetEnvironmentToken *)alloc_token (HBC_TOKEN_TYPE_GET_ENVIRONMENT, sizeof (HbcGetEnvironmentToken) - sizeof (HbcToken));
Token *create_get_environment_token(int reg_num, int nesting_level) {
GetEnvironmentToken *t = (GetEnvironmentToken *)alloc_token (TOKEN_TYPE_GET_ENVIRONMENT, sizeof (GetEnvironmentToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
t->nesting_level = nesting_level;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_load_from_environment(int reg_num, int slot_index) {
HbcLoadFromEnvironmentToken *t = (HbcLoadFromEnvironmentToken *)alloc_token (HBC_TOKEN_TYPE_LOAD_FROM_ENVIRONMENT, sizeof (HbcLoadFromEnvironmentToken) - sizeof (HbcToken));
Token *create_load_from_environment_token(int reg_num, int slot_index) {
LoadFromEnvironmentToken *t = (LoadFromEnvironmentToken *)alloc_token (TOKEN_TYPE_LOAD_FROM_ENVIRONMENT, sizeof (LoadFromEnvironmentToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
t->slot_index = slot_index;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_new_environment(int reg_num) {
HbcNewEnvironmentToken *t = (HbcNewEnvironmentToken *)alloc_token (HBC_TOKEN_TYPE_NEW_ENVIRONMENT, sizeof (HbcNewEnvironmentToken) - sizeof (HbcToken));
Token *create_new_environment_token(int reg_num) {
NewEnvironmentToken *t = (NewEnvironmentToken *)alloc_token (TOKEN_TYPE_NEW_ENVIRONMENT, sizeof (NewEnvironmentToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_new_inner_environment(int dest_register, int parent_register, int number_of_slots) {
HbcNewInnerEnvironmentToken *t = (HbcNewInnerEnvironmentToken *)alloc_token (HBC_TOKEN_TYPE_NEW_INNER_ENVIRONMENT, sizeof (HbcNewInnerEnvironmentToken) - sizeof (HbcToken));
Token *create_new_inner_environment_token(int dest_register, int parent_register, int number_of_slots) {
NewInnerEnvironmentToken *t = (NewInnerEnvironmentToken *)alloc_token (TOKEN_TYPE_NEW_INNER_ENVIRONMENT, sizeof (NewInnerEnvironmentToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->dest_register = dest_register;
t->parent_register = parent_register;
t->number_of_slots = number_of_slots;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_switch_imm(int value_reg, u32 jump_table_address, u32 default_jump_address, u32 unsigned_min_value, u32 unsigned_max_value) {
HbcSwitchImmToken *t = (HbcSwitchImmToken *)alloc_token (HBC_TOKEN_TYPE_SWITCH_IMM, sizeof (HbcSwitchImmToken) - sizeof (HbcToken));
Token *create_switch_imm_token(int value_reg, u32 jump_table_address, u32 default_jump_address, u32 unsigned_min_value, u32 unsigned_max_value) {
SwitchImmToken *t = (SwitchImmToken *)alloc_token (TOKEN_TYPE_SWITCH_IMM, sizeof (SwitchImmToken) - sizeof (Token));
if (!t) {
return NULL;
}
@@ -202,22 +202,22 @@ HbcToken *hbc_token_new_switch_imm(int value_reg, u32 jump_table_address, u32 de
t->default_jump_address = default_jump_address;
t->unsigned_min_value = unsigned_min_value;
t->unsigned_max_value = unsigned_max_value;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_store_to_environment(int env_register, int slot_index, int value_register) {
HbcStoreToEnvironmentToken *t = (HbcStoreToEnvironmentToken *)alloc_token (HBC_TOKEN_TYPE_STORE_TO_ENVIRONMENT, sizeof (HbcStoreToEnvironmentToken) - sizeof (HbcToken));
Token *create_store_to_environment_token(int env_register, int slot_index, int value_register) {
StoreToEnvironmentToken *t = (StoreToEnvironmentToken *)alloc_token (TOKEN_TYPE_STORE_TO_ENVIRONMENT, sizeof (StoreToEnvironmentToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->env_register = env_register;
t->slot_index = slot_index;
t->value_register = value_register;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_for_in_loop_init(int obj_props_register, int obj_register, int iter_index_register, int iter_size_register) {
HbcForInLoopInitToken *t = (HbcForInLoopInitToken *)alloc_token (HBC_TOKEN_TYPE_FOR_IN_LOOP_INIT, sizeof (HbcForInLoopInitToken) - sizeof (HbcToken));
Token *create_for_in_loop_init_token(int obj_props_register, int obj_register, int iter_index_register, int iter_size_register) {
ForInLoopInitToken *t = (ForInLoopInitToken *)alloc_token (TOKEN_TYPE_FOR_IN_LOOP_INIT, sizeof (ForInLoopInitToken) - sizeof (Token));
if (!t) {
return NULL;
}
@@ -225,11 +225,11 @@ HbcToken *hbc_token_new_for_in_loop_init(int obj_props_register, int obj_registe
t->obj_register = obj_register;
t->iter_index_register = iter_index_register;
t->iter_size_register = iter_size_register;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_for_in_loop_next_iter(int next_value_register, int obj_props_register, int obj_register, int iter_index_register, int iter_size_register) {
HbcForInLoopNextIterToken *t = (HbcForInLoopNextIterToken *)alloc_token (HBC_TOKEN_TYPE_FOR_IN_LOOP_NEXT_ITER, sizeof (HbcForInLoopNextIterToken) - sizeof (HbcToken));
Token *create_for_in_loop_next_iter_token(int next_value_register, int obj_props_register, int obj_register, int iter_index_register, int iter_size_register) {
ForInLoopNextIterToken *t = (ForInLoopNextIterToken *)alloc_token (TOKEN_TYPE_FOR_IN_LOOP_NEXT_ITER, sizeof (ForInLoopNextIterToken) - sizeof (Token));
if (!t) {
return NULL;
}
@@ -238,227 +238,217 @@ HbcToken *hbc_token_new_for_in_loop_next_iter(int next_value_register, int obj_p
t->obj_register = obj_register;
t->iter_index_register = iter_index_register;
t->iter_size_register = iter_size_register;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_resume_generator(int result_out_reg, int return_bool_out_reg) {
HbcResumeGeneratorToken *t = (HbcResumeGeneratorToken *)alloc_token (HBC_TOKEN_TYPE_RESUME_GENERATOR, sizeof (HbcResumeGeneratorToken) - sizeof (HbcToken));
Token *create_resume_generator_token(int result_out_reg, int return_bool_out_reg) {
ResumeGeneratorToken *t = (ResumeGeneratorToken *)alloc_token (TOKEN_TYPE_RESUME_GENERATOR, sizeof (ResumeGeneratorToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->result_out_reg = result_out_reg;
t->return_bool_out_reg = return_bool_out_reg;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_save_generator(u32 address) {
HbcSaveGeneratorToken *t = (HbcSaveGeneratorToken *)alloc_token (HBC_TOKEN_TYPE_SAVE_GENERATOR, sizeof (HbcSaveGeneratorToken) - sizeof (HbcToken));
Token *create_save_generator_token(u32 address) {
SaveGeneratorToken *t = (SaveGeneratorToken *)alloc_token (TOKEN_TYPE_SAVE_GENERATOR, sizeof (SaveGeneratorToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->address = address;
return (HbcToken *)t;
return (Token *)t;
}
HbcToken *hbc_token_new_start_generator(void) {
return alloc_token (HBC_TOKEN_TYPE_START_GENERATOR, 0);
Token *create_start_generator_token(void) {
return alloc_token (TOKEN_TYPE_START_GENERATOR, 0);
}
HbcToken *hbc_token_new_catch_block_start(int arg_register) {
HbcCatchBlockStartToken *t = (HbcCatchBlockStartToken *)alloc_token (HBC_TOKEN_TYPE_CATCH_BLOCK_START, sizeof (HbcCatchBlockStartToken) - sizeof (HbcToken));
Token *create_catch_block_start_token(int arg_register) {
CatchBlockStartToken *t = (CatchBlockStartToken *)alloc_token (TOKEN_TYPE_CATCH_BLOCK_START, sizeof (CatchBlockStartToken) - sizeof (Token));
if (!t) {
return NULL;
}
t->arg_register = arg_register;
return (HbcToken *)t;
return (Token *)t;
}
void _hbc_token_free(HbcToken *tok) {
void _hbc_token_free(Token *tok) {
if (!tok) {
return;
}
if (tok->type == HBC_TOKEN_TYPE_RAW) {
HbcRawToken *rt = (HbcRawToken *)tok;
if (tok->type == TOKEN_TYPE_RAW) {
RawToken *rt = (RawToken *)tok;
free (rt->text);
}
free (tok);
}
static Result append_reg(RStrBuf *b, int r) {
return HBC_TO_RESULT (r_strbuf_appendf (b, "r%d", r));
static Result append_reg(StringBuffer *b, int r) {
return _hbc_sb_appendf (b, "r%d", r);
}
/* Very simple token to string printer, good enough for M1 */
Result _hbc_token_to_string(HbcToken *token, RStrBuf *buffer) {
Result _hbc_token_to_string(Token *token, StringBuffer *buffer) {
if (!token || !buffer) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "_hbc_token_to_string: NULL");
}
switch (token->type) {
case HBC_TOKEN_TYPE_RAW:
case TOKEN_TYPE_RAW:
{
HbcRawToken *t = (HbcRawToken *)token;
return HBC_TO_RESULT (r_strbuf_append (buffer, t->text? t->text: ""));
RawToken *t = (RawToken *)token;
return _hbc_sb_append (buffer, t->text? t->text: "");
}
case HBC_TOKEN_TYPE_LEFT_HAND_REG:
case TOKEN_TYPE_LEFT_HAND_REG:
{
HbcLeftHandRegToken *t = (HbcLeftHandRegToken *)token;
LeftHandRegToken *t = (LeftHandRegToken *)token;
return append_reg (buffer, t->reg_num);
}
case HBC_TOKEN_TYPE_RIGHT_HAND_REG:
case TOKEN_TYPE_RIGHT_HAND_REG:
{
HbcRightHandRegToken *t = (HbcRightHandRegToken *)token;
RightHandRegToken *t = (RightHandRegToken *)token;
return append_reg (buffer, t->reg_num);
}
case HBC_TOKEN_TYPE_ASSIGNMENT: return HBC_TO_RESULT (r_strbuf_append (buffer, "="));
case HBC_TOKEN_TYPE_LEFT_PARENTHESIS: return HBC_TO_RESULT (r_strbuf_append (buffer, "("));
case HBC_TOKEN_TYPE_RIGHT_PARENTHESIS: return HBC_TO_RESULT (r_strbuf_append (buffer, ")"));
case HBC_TOKEN_TYPE_DOT_ACCESSOR: return HBC_TO_RESULT (r_strbuf_append (buffer, "."));
case HBC_TOKEN_TYPE_BIND:
case TOKEN_TYPE_ASSIGNMENT: return _hbc_sb_append (buffer, "=");
case TOKEN_TYPE_LEFT_PARENTHESIS: return _hbc_sb_append (buffer, "(");
case TOKEN_TYPE_RIGHT_PARENTHESIS: return _hbc_sb_append (buffer, ")");
case TOKEN_TYPE_DOT_ACCESSOR: return _hbc_sb_append (buffer, ".");
case TOKEN_TYPE_BIND:
{
HbcBindToken *t = (HbcBindToken *)token;
RETURN_IF_ERROR (r_strbuf_append (buffer, ".bind("));
BindToken *t = (BindToken *)token;
RETURN_IF_ERROR (_hbc_sb_append (buffer, ".bind("));
RETURN_IF_ERROR (append_reg (buffer, t->reg_num));
return HBC_TO_RESULT (r_strbuf_append (buffer, ")"));
return _hbc_sb_append (buffer, ")");
}
case HBC_TOKEN_TYPE_RETURN_DIRECTIVE: return HBC_TO_RESULT (r_strbuf_append (buffer, "return"));
case HBC_TOKEN_TYPE_THROW_DIRECTIVE: return HBC_TO_RESULT (r_strbuf_append (buffer, "throw"));
case HBC_TOKEN_TYPE_FUNCTION_TABLE_INDEX:
case TOKEN_TYPE_RETURN_DIRECTIVE: return _hbc_sb_append (buffer, "return");
case TOKEN_TYPE_THROW_DIRECTIVE: return _hbc_sb_append (buffer, "throw");
case TOKEN_TYPE_FUNCTION_TABLE_INDEX:
{
HbcFunctionTableIndexToken *t = (HbcFunctionTableIndexToken *)token;
FunctionTableIndexToken *t = (FunctionTableIndexToken *)token;
/* Try to resolve name if possible */
if (t->state && t->state->hbc_reader && t->function_id < t->state->hbc_reader->header.functionCount) {
u32 name_id = t->state->hbc_reader->function_headers[t->function_id].functionName;
const char *name = (name_id < t->state->hbc_reader->header.stringCount && t->state->hbc_reader->strings)? t->state->hbc_reader->strings[name_id]: NULL;
if (name && *name) {
RETURN_IF_ERROR (r_strbuf_append (buffer, name));
RETURN_IF_ERROR (_hbc_sb_append (buffer, name));
return SUCCESS_RESULT ();
}
}
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "fn_%u", t->function_id));
return _hbc_sb_appendf (buffer, "fn_%u", t->function_id);
}
case HBC_TOKEN_TYPE_JUMP_CONDITION:
case TOKEN_TYPE_JUMP_CONDITION:
{
HbcJumpConditionToken *t = (HbcJumpConditionToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "/* jump -> 0x%08x */", t->target_address));
JumpConditionToken *t = (JumpConditionToken *)token;
return _hbc_sb_appendf (buffer, "/* jump -> 0x%08x */", t->target_address);
}
case HBC_TOKEN_TYPE_JUMP_NOT_CONDITION:
case TOKEN_TYPE_JUMP_NOT_CONDITION:
{
HbcJumpNotConditionToken *t = (HbcJumpNotConditionToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "/* jump_if_not -> 0x%08x */", t->target_address));
JumpNotConditionToken *t = (JumpNotConditionToken *)token;
return _hbc_sb_appendf (buffer, "/* jump_if_not -> 0x%08x */", t->target_address);
}
case HBC_TOKEN_TYPE_GET_ENVIRONMENT:
case TOKEN_TYPE_GET_ENVIRONMENT:
{
HbcGetEnvironmentToken *t = (HbcGetEnvironmentToken *)token;
RETURN_IF_ERROR (r_strbuf_append (buffer, "get_env("));
GetEnvironmentToken *t = (GetEnvironmentToken *)token;
RETURN_IF_ERROR (_hbc_sb_append (buffer, "get_env("));
RETURN_IF_ERROR (append_reg (buffer, t->reg_num));
RETURN_IF_ERROR (r_strbuf_append (buffer, ", "));
RETURN_IF_ERROR (r_strbuf_appendf (buffer, "%d", t->nesting_level));
return HBC_TO_RESULT (r_strbuf_append (buffer, ")"));
RETURN_IF_ERROR (_hbc_sb_append (buffer, ", "));
RETURN_IF_ERROR (_hbc_sb_appendf (buffer, "%d", t->nesting_level));
return _hbc_sb_append (buffer, ")");
}
case HBC_TOKEN_TYPE_LOAD_FROM_ENVIRONMENT:
case TOKEN_TYPE_LOAD_FROM_ENVIRONMENT:
{
HbcLoadFromEnvironmentToken *t = (HbcLoadFromEnvironmentToken *)token;
RETURN_IF_ERROR (r_strbuf_append (buffer, "env_load("));
LoadFromEnvironmentToken *t = (LoadFromEnvironmentToken *)token;
RETURN_IF_ERROR (_hbc_sb_append (buffer, "env_load("));
RETURN_IF_ERROR (append_reg (buffer, t->reg_num));
RETURN_IF_ERROR (r_strbuf_append (buffer, ", "));
RETURN_IF_ERROR (r_strbuf_appendf (buffer, "%d", t->slot_index));
return HBC_TO_RESULT (r_strbuf_append (buffer, ")"));
RETURN_IF_ERROR (_hbc_sb_append (buffer, ", "));
RETURN_IF_ERROR (_hbc_sb_appendf (buffer, "%d", t->slot_index));
return _hbc_sb_append (buffer, ")");
}
case HBC_TOKEN_TYPE_NEW_ENVIRONMENT:
case TOKEN_TYPE_NEW_ENVIRONMENT:
{
HbcNewEnvironmentToken *t = (HbcNewEnvironmentToken *)token;
RETURN_IF_ERROR (r_strbuf_append (buffer, "new_env("));
NewEnvironmentToken *t = (NewEnvironmentToken *)token;
RETURN_IF_ERROR (_hbc_sb_append (buffer, "new_env("));
RETURN_IF_ERROR (append_reg (buffer, t->reg_num));
return HBC_TO_RESULT (r_strbuf_append (buffer, ")"));
return _hbc_sb_append (buffer, ")");
}
case HBC_TOKEN_TYPE_NEW_INNER_ENVIRONMENT:
case TOKEN_TYPE_NEW_INNER_ENVIRONMENT:
{
HbcNewInnerEnvironmentToken *t = (HbcNewInnerEnvironmentToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer,
NewInnerEnvironmentToken *t = (NewInnerEnvironmentToken *)token;
return _hbc_sb_appendf (buffer,
"new_inner_env(r%d, r%d, %d)",
t->dest_register,
t->parent_register,
t->number_of_slots));
t->dest_register, t->parent_register, t->number_of_slots);
}
case HBC_TOKEN_TYPE_SWITCH_IMM:
case TOKEN_TYPE_SWITCH_IMM:
{
HbcSwitchImmToken *t = (HbcSwitchImmToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer,
SwitchImmToken *t = (SwitchImmToken *)token;
return _hbc_sb_appendf (buffer,
"switch(r%d /*%u..%u*/)",
t->value_reg,
t->unsigned_min_value,
t->unsigned_max_value));
t->value_reg, t->unsigned_min_value, t->unsigned_max_value);
}
case HBC_TOKEN_TYPE_STORE_TO_ENVIRONMENT:
case TOKEN_TYPE_STORE_TO_ENVIRONMENT:
{
HbcStoreToEnvironmentToken *t = (HbcStoreToEnvironmentToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer,
StoreToEnvironmentToken *t = (StoreToEnvironmentToken *)token;
return _hbc_sb_appendf (buffer,
"env_store(r%d, %d, r%d)",
t->env_register,
t->slot_index,
t->value_register));
t->env_register, t->slot_index, t->value_register);
}
case HBC_TOKEN_TYPE_FOR_IN_LOOP_INIT:
case TOKEN_TYPE_FOR_IN_LOOP_INIT:
{
HbcForInLoopInitToken *t = (HbcForInLoopInitToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer,
ForInLoopInitToken *t = (ForInLoopInitToken *)token;
return _hbc_sb_appendf (buffer,
"forin_init(r%d, r%d, r%d, r%d)",
t->obj_props_register,
t->obj_register,
t->iter_index_register,
t->iter_size_register));
t->obj_props_register, t->obj_register,
t->iter_index_register, t->iter_size_register);
}
case HBC_TOKEN_TYPE_FOR_IN_LOOP_NEXT_ITER:
case TOKEN_TYPE_FOR_IN_LOOP_NEXT_ITER:
{
HbcForInLoopNextIterToken *t = (HbcForInLoopNextIterToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer,
ForInLoopNextIterToken *t = (ForInLoopNextIterToken *)token;
return _hbc_sb_appendf (buffer,
"forin_next(r%d, r%d, r%d, r%d, r%d)",
t->next_value_register,
t->obj_props_register,
t->obj_register,
t->iter_index_register,
t->iter_size_register));
t->next_value_register, t->obj_props_register,
t->obj_register, t->iter_index_register,
t->iter_size_register);
}
case HBC_TOKEN_TYPE_RESUME_GENERATOR:
case TOKEN_TYPE_RESUME_GENERATOR:
{
HbcResumeGeneratorToken *t = (HbcResumeGeneratorToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "resume_gen(r%d, r%d)", t->result_out_reg, t->return_bool_out_reg));
ResumeGeneratorToken *t = (ResumeGeneratorToken *)token;
return _hbc_sb_appendf (buffer, "resume_gen(r%d, r%d)", t->result_out_reg, t->return_bool_out_reg);
}
case HBC_TOKEN_TYPE_SAVE_GENERATOR:
case TOKEN_TYPE_SAVE_GENERATOR:
{
HbcSaveGeneratorToken *t = (HbcSaveGeneratorToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "save_gen(0x%08x)", t->address));
SaveGeneratorToken *t = (SaveGeneratorToken *)token;
return _hbc_sb_appendf (buffer, "save_gen(0x%08x)", t->address);
}
case HBC_TOKEN_TYPE_START_GENERATOR: return HBC_TO_RESULT (r_strbuf_append (buffer, "start_generator()"));
case HBC_TOKEN_TYPE_CATCH_BLOCK_START:
case TOKEN_TYPE_START_GENERATOR: return _hbc_sb_append (buffer, "start_generator()");
case TOKEN_TYPE_CATCH_BLOCK_START:
{
HbcCatchBlockStartToken *t = (HbcCatchBlockStartToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "catch(r%d)", t->arg_register));
CatchBlockStartToken *t = (CatchBlockStartToken *)token;
return _hbc_sb_appendf (buffer, "catch(r%d)", t->arg_register);
}
default:
return HBC_TO_RESULT (r_strbuf_append (buffer, "/*token*/"));
return _hbc_sb_append (buffer, "/*token*/");
}
}
Result _hbc_token_string_to_string(HbcTokenString *ts, RStrBuf *out) {
Result _hbc_token_string_to_string(TokenString *ts, StringBuffer *out) {
if (!ts || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "_hbc_token_string_to_string: NULL");
}
HbcToken *cur = ts->head;
HbcTokenType prev = (HbcTokenType) (-1);
Token *cur = ts->head;
TokenType prev = (TokenType) (-1);
while (cur) {
/* spacing rules: space before '(', none after '(' or '.' nor before ')' or '.' */
if (cur != ts->head) {
bool need_space;
if (cur->type == HBC_TOKEN_TYPE_LEFT_PARENTHESIS) {
if (cur->type == TOKEN_TYPE_LEFT_PARENTHESIS) {
need_space = true;
} else if (cur->type == HBC_TOKEN_TYPE_RIGHT_PARENTHESIS || cur->type == HBC_TOKEN_TYPE_DOT_ACCESSOR) {
} else if (cur->type == TOKEN_TYPE_RIGHT_PARENTHESIS || cur->type == TOKEN_TYPE_DOT_ACCESSOR) {
need_space = false;
} else {
need_space = prev != HBC_TOKEN_TYPE_LEFT_PARENTHESIS && prev != HBC_TOKEN_TYPE_DOT_ACCESSOR;
need_space = prev != TOKEN_TYPE_LEFT_PARENTHESIS && prev != TOKEN_TYPE_DOT_ACCESSOR;
}
if (need_space) {
RETURN_IF_ERROR (r_strbuf_appendf (out, "%c", ' '));
RETURN_IF_ERROR (_hbc_sb_append_char (out, ' '));
}
}
RETURN_IF_ERROR (_hbc_token_to_string (cur, out));
File diff suppressed because it is too large. Load diff
+93 -238
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/hbc.h>
#include <hbc/common.h>
@@ -15,40 +15,19 @@
#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
* ============================================================================ */
static Result hbc_parse_reader(HBC *hbc) {
HBCReader *reader = &hbc->reader;
RETURN_IF_ERROR (_hbc_reader_read_header (reader));
RETURN_IF_ERROR (_hbc_reader_read_functions_robust (reader));
RETURN_IF_ERROR (_hbc_reader_read_string_kinds (reader));
RETURN_IF_ERROR (_hbc_reader_read_identifier_hashes (reader));
RETURN_IF_ERROR (_hbc_reader_read_string_tables (reader));
RETURN_IF_ERROR (_hbc_reader_read_arrays (reader));
RETURN_IF_ERROR (_hbc_reader_read_bigints (reader));
RETURN_IF_ERROR (_hbc_reader_read_regexp (reader));
RETURN_IF_ERROR (_hbc_reader_read_cjs_modules (reader));
RETURN_IF_ERROR (_hbc_reader_read_function_sources (reader));
return _hbc_reader_read_debug_info_header (reader);
}
Result hbc_open(const char *path, HBC **out) {
if (!path || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for hbc_open");
}
HBC *hbc = R_NEW0 (HBC);
HBC *hbc = (HBC *)calloc (1, sizeof (HBC));
if (!hbc) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate HBC");
}
Result res = _hbc_reader_init (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
@@ -72,7 +51,10 @@ Result hbc_open_from_memory(const u8 *data, size_t size, HBC **out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for hbc_open_from_memory");
}
HBC *hbc = R_NEW0 (HBC);
HBC *hbc = (HBC *)calloc (1, sizeof (HBC));
if (!hbc) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate HBC");
}
Result res = _hbc_reader_init (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
@@ -80,48 +62,70 @@ Result hbc_open_from_memory(const u8 *data, size_t size, HBC **out) {
return res;
}
hbc->reader.file_buffer = r_buf_new_with_bytes (data, size);
if (!hbc->reader.file_buffer) {
res = _hbc_buffer_reader_init_from_memory (&hbc->reader.file_buffer, data, size);
if (res.code != RESULT_SUCCESS) {
_hbc_reader_cleanup (&hbc->reader);
free (hbc);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate buffer");
return res;
}
res = hbc_parse_reader (hbc);
/* Parse header and all sections */
res = _hbc_reader_read_header (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
*out = hbc;
return SUCCESS_RESULT ();
}
Result hbc_open_from_rbuffer(RBuffer *buf, HBC **out) {
if (!buf || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for hbc_open_from_rbuffer");
}
ut64 size = r_buf_size (buf);
if (size == 0) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid buffer size");
}
HBC *hbc = R_NEW0 (HBC);
Result res = _hbc_reader_init (&hbc->reader);
res = _hbc_reader_read_functions_robust (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
free (hbc);
hbc_close (hbc);
return res;
}
hbc->reader.file_buffer = r_buf_new_slice (buf, 0, size);
if (!hbc->reader.file_buffer) {
_hbc_reader_cleanup (&hbc->reader);
free (hbc);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to create buffer view");
res = _hbc_reader_read_string_kinds (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
res = hbc_parse_reader (hbc);
res = _hbc_reader_read_identifier_hashes (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
res = _hbc_reader_read_string_tables (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
res = _hbc_reader_read_arrays (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
res = _hbc_reader_read_bigints (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
res = _hbc_reader_read_regexp (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
res = _hbc_reader_read_cjs_modules (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
res = _hbc_reader_read_function_sources (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
/* Read only the DebugInfoHeader eagerly — a handful of u32s — so
* hbc_has_source_lines () can answer accurately without committing to the
* full body parse. The filename, file region and sources/scope/textified
* storage blobs are read lazily on first hbc_get_source_lines /
* hbc_get_debug_info call. */
res = _hbc_reader_read_debug_info_header (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
@@ -136,7 +140,6 @@ void hbc_close(HBC *hbc) {
return;
}
hbc_literals_reset (hbc);
free (hbc->function_flags);
_hbc_reader_cleanup (&hbc->reader);
free (hbc);
}
@@ -163,89 +166,6 @@ 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");
@@ -266,57 +186,6 @@ 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 ();
}
@@ -622,7 +491,7 @@ Result hbc_get_function_bytecode(HBC *hbc, u32 function_id, const u8 **out_ptr,
}
*out_ptr = NULL;
*out_size = 0;
if (!hbc->reader.file_buffer || !hbc->reader.function_headers) {
if (!hbc->reader.file_buffer.data || !hbc->reader.function_headers) {
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "Bytecode data is not loaded");
}
if (function_id >= hbc->reader.header.functionCount) {
@@ -635,30 +504,10 @@ Result hbc_get_function_bytecode(HBC *hbc, u32 function_id, const u8 **out_ptr,
if (size == 0) {
size = _hbc_reader_resolve_deduped_size (&hbc->reader, fh);
}
ut64 file_size = r_buf_size (hbc->reader.file_buffer);
if (fh->offset > file_size || size > file_size - fh->offset) {
if (fh->offset > hbc->reader.file_buffer.size || size > hbc->reader.file_buffer.size - fh->offset) {
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "Function bytecode range is out of bounds");
}
const u8 *file_bytes = NULL;
if (hbc->reader.file_buffer->type == R_BUFFER_BYTES && hbc->reader.file_buffer->rb_bytes && hbc->reader.file_buffer->rb_bytes->buf) {
file_bytes = hbc->reader.file_buffer->rb_bytes->buf;
} else {
if (file_size > SIZE_MAX) {
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "File is too large");
}
if (!hbc->reader.file_bytes_cache) {
hbc->reader.file_bytes_cache = (u8 *)malloc ((size_t)file_size);
if (!hbc->reader.file_bytes_cache) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate file cache");
}
if ((ut64)r_buf_read_at (hbc->reader.file_buffer, 0, hbc->reader.file_bytes_cache, file_size) != file_size) {
R_FREE (hbc->reader.file_bytes_cache);
return ERROR_RESULT (RESULT_ERROR_READ, "Failed to cache file buffer");
}
}
file_bytes = hbc->reader.file_bytes_cache;
}
*out_ptr = file_bytes + fh->offset;
*out_ptr = hbc->reader.file_buffer.data + fh->offset;
*out_size = size;
return SUCCESS_RESULT ();
}
@@ -676,18 +525,19 @@ Result hbc_decomp_fn(
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
}
RStrBuf sb;
r_strbuf_init (&sb);
Result res = _hbc_decompile_function_with_state (hbc, function_id, options, &sb);
StringBuffer sb;
Result res = _hbc_sb_init (&sb, 16 * 1024);
if (res.code != RESULT_SUCCESS) {
r_strbuf_fini (&sb);
return res;
}
*out_str = r_strbuf_drain_nofree (&sb);
if (!*out_str) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to drain decompilation output");
res = _hbc_decompile_function_with_state (hbc, function_id, options, &sb);
if (res.code != RESULT_SUCCESS) {
_hbc_sb_free (&sb);
return res;
}
*out_str = sb.data;
return SUCCESS_RESULT ();
}
@@ -699,18 +549,19 @@ Result hbc_decomp_all(
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
}
RStrBuf sb;
r_strbuf_init (&sb);
Result res = _hbc_decompile_all_with_state (hbc, options, &sb);
StringBuffer sb;
Result res = _hbc_sb_init (&sb, 32 * 1024);
if (res.code != RESULT_SUCCESS) {
r_strbuf_fini (&sb);
return res;
}
*out_str = r_strbuf_drain_nofree (&sb);
if (!*out_str) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to drain decompilation output");
res = _hbc_decompile_all_with_state (hbc, options, &sb);
if (res.code != RESULT_SUCCESS) {
_hbc_sb_free (&sb);
return res;
}
*out_str = sb.data;
return SUCCESS_RESULT ();
}
@@ -809,7 +660,7 @@ Result hbc_find_eval_sites(HBC *hbc, HBCEvalSites *out) {
.function_address = info.offset,
.function_name = info.name,
.strict = inst->operands[2].operand_type != OPERAND_TYPE_NONE && code[pc + 3] != 0,
};
};
Result res = hbc_eval_sites_add (out, &cap, &site);
if (res.code != RESULT_SUCCESS) {
hbc_free_eval_sites (out);
@@ -835,8 +686,10 @@ void hbc_free_eval_sites(HBCEvalSites *sites) {
static char *build_literal_comment(HBCReader *reader, const char *mnemonic, const u32 *ovs, const char *base_text) {
const bool is_obj = mnemonic && !strncmp (mnemonic, "new_object_with_buffer", strlen ("new_object_with_buffer"));
RStrBuf sb;
r_strbuf_init (&sb);
StringBuffer sb;
if (_hbc_sb_init (&sb, 256).code != RESULT_SUCCESS) {
return NULL;
}
Result fmt_res;
if (is_obj) {
const u32 key_count = ovs[1];
@@ -850,21 +703,23 @@ static char *build_literal_comment(HBCReader *reader, const char *mnemonic, cons
fmt_res = _hbc_format_array_literal (reader, value_count, array_id, &sb, LITERALS_PRETTY_NEVER, true);
}
char *result = NULL;
int sb_len = r_strbuf_length (&sb);
const char *sb_text = r_strbuf_get (&sb);
if (fmt_res.code == RESULT_SUCCESS && sb_text && sb_len > 0) {
if (fmt_res.code == RESULT_SUCCESS && sb.length > 0) {
size_t base_len = strlen (base_text);
result = (char *)malloc (base_len + 4 + (size_t)sb_len + 1);
result = (char *)malloc (base_len + 4 + sb.length + 1);
if (result) {
memcpy (result, base_text, base_len);
memcpy (result + base_len, " ; ", 4);
memcpy (result + base_len + 4, sb_text, (size_t)sb_len + 1);
memcpy (result + base_len + 4, sb.data, sb.length + 1);
}
}
r_strbuf_fini (&sb);
_hbc_sb_free (&sb);
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");
+21 -13
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef HBC_INTERNAL_H
#define HBC_INTERNAL_H
@@ -6,22 +6,32 @@
#include <hbc/hbc.h>
#include <hbc/parser.h>
#include <hbc/literals.h>
#include <r_vec.h>
#include <stdlib.h>
#include <string.h>
static inline void hbc_literal_entry_fini(HBCLiteralEntry *entry) {
if (!entry) {
return;
/* Grow a dynamic array (doubling) so it has room for one more element.
* arrp points at the array pointer (e.g. &vec->data); *cap is the capacity in
* elements; initial is the capacity to allocate when growing from empty. The
* stored pointer is read/written via memcpy so no aliasing cast is needed. */
static inline Result grow_array(void *arrp, u32 *cap, u32 count, size_t elem_size, u32 initial) {
if (count < *cap) {
return SUCCESS_RESULT ();
}
free (entry->formatted);
free (entry->xref_addrs);
u32 new_cap = *cap? *cap * 2: initial;
void *cur;
memcpy (&cur, arrp, sizeof (cur));
void *grown = realloc (cur, (size_t)new_cap * elem_size);
if (!grown) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "out of memory");
}
memcpy (arrp, &grown, sizeof (grown));
*cap = new_cap;
return SUCCESS_RESULT ();
}
R_VEC_TYPE_WITH_FINI(RVecHBCLiteralEntry, HBCLiteralEntry, hbc_literal_entry_fini)
typedef struct {
RVecHBCLiteralEntry entries;
HBCLiteralEntry *entries;
u32 count;
u32 cap;
} HBCLiteralCache;
struct HBC {
@@ -31,8 +41,6 @@ struct HBC {
u32 string_count;
const char **strings;
u32 function_count;
u32 *function_flags;
bool function_flags_ready;
HBCLiteralCache lit_cache;
};
+71 -36
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* Public literals API: lazy cache, formatting, scanning. See
* include/hbc/literals.h for the contract. */
@@ -12,12 +12,6 @@
#include <stdlib.h>
#include <string.h>
R_VEC_TYPE(RVecHBCPoolGroup, HBCPoolGroup)
static HBCPoolGroup hbc_pool_group(u32 paddr, u32 pool_offset, u32 num_items, u8 tag) {
return (HBCPoolGroup){ .paddr = paddr, .pool_offset = pool_offset, .num_items = num_items, .tag = tag };
}
/* Little-endian readers for raw bytecode operands. */
static inline u32 rd_u16le(const u8 *p) {
return (u32)p[0] | ((u32)p[1] << 8);
@@ -78,8 +72,11 @@ u32 hbc_get_object_values_size(HBC *hbc) {
static Result format_raw_impl(HBCReader *r, HBCLiteralKind kind, u32 num_items, u32 primary_id, u32 secondary_id, char **out) {
*out = NULL;
RStrBuf sb;
r_strbuf_init (&sb);
StringBuffer sb;
Result ir = _hbc_sb_init (&sb, 128);
if (ir.code != RESULT_SUCCESS) {
return ir;
}
Result fr;
if (kind == HBC_LIT_ARRAY) {
fr = _hbc_format_array_literal (r, num_items, primary_id, &sb, LITERALS_PRETTY_NEVER, true);
@@ -88,14 +85,13 @@ static Result format_raw_impl(HBCReader *r, HBCLiteralKind kind, u32 num_items,
} else {
fr = _hbc_format_object_literal (r, num_items, num_items, primary_id, secondary_id, &sb, LITERALS_PRETTY_NEVER, true);
}
const char *text = r_strbuf_get (&sb);
if (fr.code == RESULT_SUCCESS && text && r_strbuf_length (&sb) > 0) {
*out = strdup (text);
if (fr.code == RESULT_SUCCESS && sb.data && sb.length > 0) {
*out = strdup (sb.data);
if (!*out) {
fr = ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "oom");
}
}
r_strbuf_fini (&sb);
_hbc_sb_free (&sb);
return fr;
}
@@ -125,7 +121,8 @@ Result hbc_literals_format_for_opcode(HBC *hbc, u8 opcode, u32 arg3, u32 arg4, u
/* ---------- Cache ---------- */
static HBCLiteralEntry *cache_find(HBCLiteralCache *c, HBCLiteralKind kind, u32 num_items, u32 primary_id, u32 secondary_id) {
for (HBCLiteralEntry *e = R_VEC_START_ITER (&c->entries); e != R_VEC_END_ITER (&c->entries); e++) {
for (u32 i = 0; i < c->count; i++) {
HBCLiteralEntry *e = &c->entries[i];
if (e->kind == kind && e->num_items == num_items && e->primary_id == primary_id && e->secondary_id == secondary_id) {
return e;
}
@@ -133,8 +130,18 @@ static HBCLiteralEntry *cache_find(HBCLiteralCache *c, HBCLiteralKind kind, u32
return NULL;
}
/* Anchor objects at their values, not their shared keys/shape. */
static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id, u32 secondary_id) {
static HBCLiteralEntry *cache_alloc(HBCLiteralCache *c) {
if (grow_array (&c->entries, &c->cap, c->count, sizeof (HBCLiteralEntry), 64).code != RESULT_SUCCESS) {
return NULL;
}
HBCLiteralEntry *e = &c->entries[c->count++];
memset (e, 0, sizeof (*e));
return e;
}
/* 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) {
const HBCReader *r = &hbc->reader;
if (kind == HBC_LIT_ARRAY) {
if (!r->arrays) {
@@ -145,10 +152,18 @@ static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id, u32 second
}
return r->arrays_paddr + primary_id;
}
if (!r->object_values || secondary_id >= r->header.objValueBufferSize) {
/* 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;
}
return 0;
}
return r->object_values_paddr + secondary_id;
if (!r->object_keys || primary_id >= r->header.objKeyBufferSize) {
return 0;
}
return r->object_keys_paddr + primary_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) {
@@ -164,16 +179,15 @@ static Result cache_get_or_create(HBC *hbc, HBCLiteralKind kind, u32 num_items,
*out_entry = e;
return SUCCESS_RESULT ();
}
e = RVecHBCLiteralEntry_emplace_back (&hbc->lit_cache.entries);
e = cache_alloc (&hbc->lit_cache);
if (!e) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "oom");
}
memset (e, 0, sizeof (*e));
e->kind = kind;
e->num_items = num_items;
e->primary_id = primary_id;
e->secondary_id = secondary_id;
e->paddr = entry_paddr (hbc, kind, primary_id, secondary_id);
e->paddr = entry_paddr (hbc, kind, primary_id);
if (format) {
char *txt = NULL;
Result fr = format_raw_impl (&hbc->reader, kind, num_items, primary_id, secondary_id, &txt);
@@ -190,7 +204,10 @@ Result hbc_literals_get(HBC *hbc, HBCLiteralKind kind, u32 num_items, u32 primar
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "invalid args");
}
HBCLiteralEntry *e;
RETURN_IF_ERROR (cache_get_or_create (hbc, kind, num_items, primary_id, secondary_id, true, &e));
Result r = cache_get_or_create (hbc, kind, num_items, primary_id, secondary_id, true, &e);
if (r.code != RESULT_SUCCESS) {
return r;
}
*out_formatted = e->formatted;
return SUCCESS_RESULT ();
}
@@ -215,7 +232,10 @@ static Result register_literal(HBC *hbc, HBCLiteralKind kind, u32 num_items, u32
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "invalid args");
}
HBCLiteralEntry *e;
RETURN_IF_ERROR (cache_get_or_create (hbc, kind, num_items, primary_id, secondary_id, format, &e));
Result r = cache_get_or_create (hbc, kind, num_items, primary_id, secondary_id, format, &e);
if (r.code != RESULT_SUCCESS) {
return r;
}
if (from_addr) {
xref_add (e, from_addr);
}
@@ -230,13 +250,13 @@ Result hbc_literals_list(HBC *hbc, const HBCLiteralEntry **out, u32 *out_count)
if (!hbc || !out || !out_count) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "invalid args");
}
*out = R_VEC_START_ITER (&hbc->lit_cache.entries);
*out_count = (u32)RVecHBCLiteralEntry_length (&hbc->lit_cache.entries);
*out = hbc->lit_cache.entries;
*out_count = hbc->lit_cache.count;
return SUCCESS_RESULT ();
}
u32 hbc_literals_count(HBC *hbc) {
return hbc? (u32)RVecHBCLiteralEntry_length (&hbc->lit_cache.entries): 0;
return hbc? hbc->lit_cache.count: 0;
}
void hbc_literals_reset(HBC *hbc) {
@@ -244,8 +264,14 @@ void hbc_literals_reset(HBC *hbc) {
return;
}
HBCLiteralCache *c = &hbc->lit_cache;
RVecHBCLiteralEntry_fini (&c->entries);
RVecHBCLiteralEntry_init (&c->entries);
for (u32 i = 0; i < c->count; i++) {
free (c->entries[i].formatted);
free (c->entries[i].xref_addrs);
}
free (c->entries);
c->entries = NULL;
c->count = 0;
c->cap = 0;
}
/* ---------- Code-side scan ---------- */
@@ -462,7 +488,7 @@ static Result scan_code(HBC *hbc, bool filter_kind, HBCLiteralKind want_kind, bo
}
}
if (out_count) {
*out_count = (u32)RVecHBCLiteralEntry_length (&hbc->lit_cache.entries);
*out_count = hbc->lit_cache.count;
}
return SUCCESS_RESULT ();
}
@@ -556,8 +582,12 @@ Result hbc_literals_scan_pool(HBC *hbc, HBCLiteralKind kind, HBCPoolGroup **out,
return SUCCESS_RESULT ();
}
RVecHBCPoolGroup groups;
RVecHBCPoolGroup_init (&groups);
u32 cap = 64;
HBCPoolGroup *arr = calloc (cap, sizeof (HBCPoolGroup));
if (!arr) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "oom");
}
u32 n = 0;
size_t pos = 0;
while (pos < size) {
size_t group_start = pos;
@@ -566,11 +596,16 @@ Result hbc_literals_scan_pool(HBC *hbc, HBCLiteralKind kind, HBCPoolGroup **out,
if (!slp_skip_group (base, size, &pos, &tag, &length)) {
break;
}
HBCPoolGroup group = hbc_pool_group (paddr + (u32)group_start, (u32)group_start, length, tag);
RVecHBCPoolGroup_push_back (&groups, &group);
if (grow_array (&arr, &cap, n, sizeof (HBCPoolGroup), 64).code != RESULT_SUCCESS) {
break;
}
arr[n].pool_offset = (u32)group_start;
arr[n].paddr = paddr + (u32)group_start;
arr[n].num_items = length;
arr[n].tag = tag;
n++;
}
*out = R_VEC_START_ITER (&groups);
*out_count = (u32)RVecHBCPoolGroup_length (&groups);
memset (&groups, 0, sizeof (groups));
*out = arr;
*out_count = n;
return SUCCESS_RESULT ();
}
+113 -104
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/disasm.h>
#include <stdio.h>
@@ -18,14 +18,13 @@ Result _hbc_disassembler_init(Disassembler *disassembler, HBCReader *reader, HBC
}
disassembler->current_function_id = 0;
r_strbuf_init (&disassembler->output);
return SUCCESS_RESULT ();
return _hbc_sb_init (&disassembler->output, 8192);
}
/* Clean up disassembler */
void _hbc_disassembler_cleanup(Disassembler *disassembler) {
if (disassembler) {
r_strbuf_fini (&disassembler->output);
_hbc_sb_free (&disassembler->output);
}
}
@@ -36,7 +35,7 @@ Result _hbc_print_function_header(Disassembler *disassembler, FunctionHeader *fu
}
HBCReader *reader = disassembler->reader;
RStrBuf *output = &disassembler->output;
StringBuffer *output = &disassembler->output;
bool verbose = disassembler->options.verbose;
/* Get function name with validation */
@@ -49,19 +48,16 @@ Result _hbc_print_function_header(Disassembler *disassembler, FunctionHeader *fu
}
if (verbose) {
RETURN_IF_ERROR (r_strbuf_appendf (output,
RETURN_IF_ERROR (_hbc_sb_appendf (output,
"=> [Function #%u \"%s\" of %u bytes]: %u params, frame size=%u, env size=%u",
function_id,
function_name,
function_header->bytecodeSizeInBytes,
function_header->paramCount,
function_header->frameSize,
function_id, function_name, function_header->bytecodeSizeInBytes,
function_header->paramCount, function_header->frameSize,
function_header->environmentSize));
}
/* Verbose information */
if (verbose) {
RETURN_IF_ERROR (r_strbuf_appendf (output,
RETURN_IF_ERROR (_hbc_sb_appendf (output,
", read index sz=%u, write index sz=%u, strict=%s, exc handler=%s, debug info=%s",
function_header->highestReadCacheIndex,
function_header->highestWriteCacheIndex,
@@ -71,38 +67,35 @@ Result _hbc_print_function_header(Disassembler *disassembler, FunctionHeader *fu
}
/* Offset information */
RETURN_IF_ERROR (r_strbuf_appendf (output, " @ offset 0x%08x", function_header->offset));
RETURN_IF_ERROR (_hbc_sb_appendf (output, " @ offset 0x%08x", function_header->offset));
/* Exception handler information */
if (function_header->hasExceptionHandler && disassembler->options.show_debug_info) {
RETURN_IF_ERROR (r_strbuf_append (output, "\n [Exception handlers:"));
RETURN_IF_ERROR (_hbc_sb_append (output, "\n [Exception handlers:"));
ExceptionHandlerList *exc_handlers = &reader->function_id_to_exc_handlers[function_id];
for (u32 i = 0; i < exc_handlers->count; i++) {
ExceptionHandlerInfo *handler = &exc_handlers->handlers[i];
RETURN_IF_ERROR (r_strbuf_appendf (output,
RETURN_IF_ERROR (_hbc_sb_appendf (output,
" [start=0x%x, end=0x%x, target=0x%x]",
handler->start,
handler->end,
handler->target));
handler->start, handler->end, handler->target));
}
RETURN_IF_ERROR (r_strbuf_append (output, " ]"));
RETURN_IF_ERROR (_hbc_sb_append (output, " ]"));
}
/* Debug information */
if (function_header->hasDebugInfo && disassembler->options.show_debug_info) {
DebugOffsets *debug_offsets = &reader->function_id_to_debug_offsets[function_id];
RETURN_IF_ERROR (r_strbuf_appendf (output,
RETURN_IF_ERROR (_hbc_sb_appendf (output,
"\n [Debug offsets: source_locs=0x%x, scope_desc_data=0x%x]",
debug_offsets->source_locations,
debug_offsets->scope_desc_data));
debug_offsets->source_locations, debug_offsets->scope_desc_data));
}
/* End the function header */
RETURN_IF_ERROR (r_strbuf_append (output, "\n\n"));
RETURN_IF_ERROR (_hbc_sb_append (output, "\n\n"));
return SUCCESS_RESULT ();
}
@@ -110,7 +103,7 @@ Result _hbc_print_function_header(Disassembler *disassembler, FunctionHeader *fu
/* Print a single instruction */
/* Helpers for asm-syntax formatting */
static Result format_operand_asm(Disassembler *d, ParsedInstruction *ins, int idx, RStrBuf *out) {
static Result format_operand_asm(Disassembler *d, ParsedInstruction *ins, int idx, StringBuffer *out) {
OperandType t = ins->inst->operands[idx].operand_type;
OperandMeaning m = ins->inst->operands[idx].operand_meaning;
u32 v = hbc_operand_value (ins, idx);
@@ -119,15 +112,15 @@ static Result format_operand_asm(Disassembler *d, ParsedInstruction *ins, int id
FunctionHeader *fh = &r->function_headers[d->current_function_id];
if (t == OPERAND_TYPE_REG8 || t == OPERAND_TYPE_REG32) {
return HBC_TO_RESULT (r_strbuf_appendf (out, "r%u", v));
return _hbc_sb_appendf (out, "r%u", v);
}
if (m == OPERAND_MEANING_FUNCTION_ID) {
if (v < r->header.functionCount) {
u32 off = r->function_headers[v].offset;
return HBC_TO_RESULT (r_strbuf_appendf (out, "0x%x", off));
return _hbc_sb_appendf (out, "0x%x", off);
}
return HBC_TO_RESULT (r_strbuf_appendf (out, "%u", v));
return _hbc_sb_appendf (out, "%u", v);
}
if (m == OPERAND_MEANING_STRING_ID) {
@@ -147,44 +140,44 @@ static Result format_operand_asm(Disassembler *d, ParsedInstruction *ins, int id
}
}
u32 abs = r->string_storage_file_offset + off;
return HBC_TO_RESULT (r_strbuf_appendf (out, "0x%x", abs));
return _hbc_sb_appendf (out, "0x%x", abs);
}
if (t == OPERAND_TYPE_ADDR8 || t == OPERAND_TYPE_ADDR32) {
/* Convert relative address to file-absolute */
u32 file_abs = fh->offset + ins->original_pos + v;
return HBC_TO_RESULT (r_strbuf_appendf (out, "0x%x", file_abs));
return _hbc_sb_appendf (out, "0x%x", file_abs);
}
if (t == OPERAND_TYPE_DOUBLE) {
double double_val = ins->double_arg2;
return HBC_TO_RESULT (r_strbuf_appendf (out, "%.6f", double_val));
return _hbc_sb_appendf (out, "%.6f", double_val);
}
/* Default: decimal immediate */
return HBC_TO_RESULT (r_strbuf_appendf (out, "%u", v));
return _hbc_sb_appendf (out, "%u", v);
}
static Result print_instruction_asm(Disassembler *disassembler, ParsedInstruction *instruction) {
RStrBuf *out = &disassembler->output;
StringBuffer *out = &disassembler->output;
/* Prefix with absolute file address of the instruction */
HBCReader *r = disassembler->reader;
FunctionHeader *fh = &r->function_headers[disassembler->current_function_id];
RETURN_IF_ERROR (r_strbuf_appendf (out, "0x%08x: ", fh->offset + instruction->original_pos));
RETURN_IF_ERROR (_hbc_sb_appendf (out, "0x%08x: ", fh->offset + instruction->original_pos));
/* mnemonic — tables are already snake_case */
RETURN_IF_ERROR (r_strbuf_append (out, instruction->inst->name));
RETURN_IF_ERROR (_hbc_sb_append (out, instruction->inst->name));
bool first = true;
for (int i = 0; i < 6; i++) {
if (instruction->inst->operands[i].operand_type == OPERAND_TYPE_NONE) {
continue;
}
RETURN_IF_ERROR (r_strbuf_append (out, first? " ": ", "));
RETURN_IF_ERROR (_hbc_sb_append (out, first? " ": ", "));
first = false;
RETURN_IF_ERROR (format_operand_asm (disassembler, instruction, i, out));
}
RETURN_IF_ERROR (r_strbuf_append (out, "\n"));
RETURN_IF_ERROR (_hbc_sb_append (out, "\n"));
return SUCCESS_RESULT ();
}
@@ -197,14 +190,14 @@ Result _hbc_print_instruction(Disassembler *disassembler, ParsedInstruction *ins
if (disassembler->options.asm_syntax) {
return print_instruction_asm (disassembler, instruction);
}
RStrBuf *output = &disassembler->output;
StringBuffer *output = &disassembler->output;
HBCReader *reader = disassembler->reader;
FunctionHeader *fh = &reader->function_headers[disassembler->current_function_id];
/* Print instruction address */
RETURN_IF_ERROR (r_strbuf_appendf (output, "==> %08x>: <", fh->offset + instruction->original_pos));
RETURN_IF_ERROR (r_strbuf_append (output, instruction->inst->name));
RETURN_IF_ERROR (r_strbuf_append (output, ">: <"));
RETURN_IF_ERROR (_hbc_sb_appendf (output, "==> %08x>: <", fh->offset + instruction->original_pos));
RETURN_IF_ERROR (_hbc_sb_append (output, instruction->inst->name));
RETURN_IF_ERROR (_hbc_sb_append (output, ">: <"));
/* Print operands */
bool first = true;
@@ -215,29 +208,29 @@ Result _hbc_print_instruction(Disassembler *disassembler, ParsedInstruction *ins
}
if (!first) {
RETURN_IF_ERROR (r_strbuf_append (output, ", "));
RETURN_IF_ERROR (_hbc_sb_append (output, ", "));
}
first = false;
/* Print operand name and value */
const char *operand_name = hbc_operand_name (&instruction->inst->operands[i]);
RETURN_IF_ERROR (r_strbuf_append (output, operand_name));
RETURN_IF_ERROR (r_strbuf_append (output, ": "));
RETURN_IF_ERROR (_hbc_sb_append (output, operand_name));
RETURN_IF_ERROR (_hbc_sb_append (output, ": "));
/* Get operand value */
u32 value = hbc_operand_value (instruction, i);
/* Print operand value */
if (instruction->inst->operands[i].operand_type != OPERAND_TYPE_DOUBLE) {
RETURN_IF_ERROR (r_strbuf_appendf (output, "%u", value));
RETURN_IF_ERROR (_hbc_sb_append_int (output, value));
} else {
double double_val = instruction->double_arg2;
RETURN_IF_ERROR (r_strbuf_appendf (output, "%.6f", double_val));
RETURN_IF_ERROR (_hbc_sb_appendf (output, "%.6f", double_val));
}
}
RETURN_IF_ERROR (r_strbuf_append (output, ">"));
RETURN_IF_ERROR (_hbc_sb_append (output, ">"));
/* Add comments for special operands */
@@ -253,22 +246,22 @@ Result _hbc_print_instruction(Disassembler *disassembler, ParsedInstruction *ins
case OPERAND_MEANING_STRING_ID:
if (value < reader->header.stringCount &&
reader->strings && reader->strings[value]) {
RETURN_IF_ERROR (r_strbuf_append (output, " # String: \""));
RETURN_IF_ERROR (r_strbuf_append (output, reader->strings[value]));
RETURN_IF_ERROR (r_strbuf_append (output, "\" ("));
RETURN_IF_ERROR (_hbc_sb_append (output, " # String: \""));
RETURN_IF_ERROR (_hbc_sb_append (output, reader->strings[value]));
RETURN_IF_ERROR (_hbc_sb_append (output, "\" ("));
if (value < reader->header.stringCount && reader->string_kinds) {
RETURN_IF_ERROR (r_strbuf_append (output, _hbc_string_kind_to_string (reader->string_kinds[value])));
RETURN_IF_ERROR (_hbc_sb_append (output, _hbc_string_kind_to_string (reader->string_kinds[value])));
} else {
RETURN_IF_ERROR (r_strbuf_append (output, "Unknown"));
RETURN_IF_ERROR (_hbc_sb_append (output, "Unknown"));
}
RETURN_IF_ERROR (r_strbuf_append (output, ")"));
RETURN_IF_ERROR (_hbc_sb_append (output, ")"));
}
break;
case OPERAND_MEANING_BIGINT_ID:
if (value < reader->bigint_count) {
RETURN_IF_ERROR (r_strbuf_append (output, " # BigInt: "));
RETURN_IF_ERROR (r_strbuf_appendf (output, "%lld", (long long)reader->bigint_values[value]));
RETURN_IF_ERROR (_hbc_sb_append (output, " # BigInt: "));
RETURN_IF_ERROR (_hbc_sb_append_int (output, reader->bigint_values[value]));
}
break;
@@ -281,13 +274,10 @@ Result _hbc_print_instruction(Disassembler *disassembler, ParsedInstruction *ins
func_name = reader->strings[func->functionName];
}
RETURN_IF_ERROR (r_strbuf_appendf (output,
RETURN_IF_ERROR (_hbc_sb_appendf (output,
" # Function: [#%u %s of %u bytes]: %u params @ offset 0x%08x",
value,
func_name,
func->bytecodeSizeInBytes,
func->paramCount,
func->offset));
value, func_name, func->bytecodeSizeInBytes,
func->paramCount, func->offset));
}
break;
@@ -309,28 +299,28 @@ Result _hbc_print_instruction(Disassembler *disassembler, ParsedInstruction *ins
u32 value = hbc_operand_value (instruction, i);
RETURN_IF_ERROR (r_strbuf_appendf (output, " # Address: %08x", fh->offset + instruction->original_pos + value));
RETURN_IF_ERROR (_hbc_sb_appendf (output, " # Address: %08x", fh->offset + instruction->original_pos + value));
}
/* Add jump table comment for switch instructions */
if (strcmp (instruction->inst->name, "SwitchImm") == 0) {
if (instruction->switch_jump_table && instruction->switch_jump_table_size > 0) {
RETURN_IF_ERROR (r_strbuf_append (output, " # Jump table: ["));
RETURN_IF_ERROR (_hbc_sb_append (output, " # Jump table: ["));
for (u32 i = 0; i < instruction->switch_jump_table_size; i++) {
if (i > 0) {
RETURN_IF_ERROR (r_strbuf_append (output, ", "));
RETURN_IF_ERROR (_hbc_sb_append (output, ", "));
}
RETURN_IF_ERROR (r_strbuf_appendf (output, "%08x", fh->offset + instruction->switch_jump_table[i]));
RETURN_IF_ERROR (_hbc_sb_appendf (output, "%08x", fh->offset + instruction->switch_jump_table[i]));
}
RETURN_IF_ERROR (r_strbuf_append (output, "]"));
RETURN_IF_ERROR (_hbc_sb_append (output, "]"));
}
}
/* End the instruction */
RETURN_IF_ERROR (r_strbuf_append (output, "\n"));
RETURN_IF_ERROR (_hbc_sb_append (output, "\n"));
return SUCCESS_RESULT ();
}
@@ -354,9 +344,9 @@ Result _hbc_disassemble_function(Disassembler *disassembler, u32 function_id) {
/* Print bytecode listing header */
if (disassembler->options.asm_syntax) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "Bytecode listing (asm):\n\n"));
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "Bytecode listing (asm):\n\n"));
} else {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "Bytecode listing:\n\n"));
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "Bytecode listing:\n\n"));
}
/* Debug mode - always show function offset info */
@@ -372,57 +362,71 @@ Result _hbc_disassemble_function(Disassembler *disassembler, u32 function_id) {
if (function_header->bytecodeSizeInBytes > 0 && !function_header->bytecode) {
/* Skip functions with suspicious sizes */
if (function_header->bytecodeSizeInBytes > 1024 * 1024) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output,
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output,
"[Skipping function with unreasonably large bytecode size]\n"));
return SUCCESS_RESULT ();
}
/* Skip functions with offset 0 (likely invalid) */
if (function_header->offset == 0) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output,
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output,
"[No bytecode available for this function (invalid offset)]\n"));
return SUCCESS_RESULT ();
}
/* Verify offset is within file bounds */
if (function_header->offset >= r_buf_size (reader->file_buffer)) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output,
if (function_header->offset >= reader->file_buffer.size) {
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output,
"[Bytecode offset beyond file size]\n"));
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Bytecode offset beyond file size");
}
/* Verify we can read the full bytecode from the file */
if (function_header->bytecodeSizeInBytes > r_buf_size (reader->file_buffer) - function_header->offset) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output,
if (function_header->bytecodeSizeInBytes > reader->file_buffer.size - function_header->offset) {
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output,
"[Bytecode extends beyond file size, truncating]\n"));
function_header->bytecodeSizeInBytes = r_buf_size (reader->file_buffer) - function_header->offset;
function_header->bytecodeSizeInBytes = reader->file_buffer.size - function_header->offset;
}
/* Allocate bytecode buffer */
function_header->bytecode = (u8 *)malloc (function_header->bytecodeSizeInBytes);
if (!function_header->bytecode) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "[Memory allocation failed for bytecode]\n"));
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "[Memory allocation failed for bytecode]\n"));
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate bytecode buffer");
}
/* Read bytecode data */
bool read_ok = (u32)r_buf_read_at (reader->file_buffer,
function_header->offset,
function_header->bytecode,
function_header->bytecodeSizeInBytes) == function_header->bytecodeSizeInBytes;
/* Save current position */
size_t saved_pos = reader->file_buffer.position;
if (!read_ok) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "[Failed to read bytecode data]\n"));
/* Seek to bytecode offset */
Result seek_result = _hbc_buffer_reader_seek (&reader->file_buffer, function_header->offset);
if (seek_result.code != RESULT_SUCCESS) {
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "[Failed to seek to bytecode location]\n"));
free (function_header->bytecode);
function_header->bytecode = NULL;
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Failed to read bytecode data");
return seek_result;
}
/* Read bytecode data */
Result read_result = _hbc_buffer_reader_read_bytes (&reader->file_buffer,
function_header->bytecode,
function_header->bytecodeSizeInBytes);
/* Restore original position */
_hbc_buffer_reader_seek (&reader->file_buffer, saved_pos);
if (read_result.code != RESULT_SUCCESS) {
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "[Failed to read bytecode data]\n"));
free (function_header->bytecode);
function_header->bytecode = NULL;
return read_result;
}
/* Verify the first byte looks like a valid opcode */
if (function_header->bytecodeSizeInBytes > 0) {
u8 first_opcode = function_header->bytecode[0];
if (first_opcode == 0 || first_opcode > 0xA2) { // Using known opcode range
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output,
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output,
"[Warning: First byte doesn't look like a valid opcode]\n"));
}
}
@@ -435,28 +439,26 @@ Result _hbc_disassemble_function(Disassembler *disassembler, u32 function_id) {
if (result.code != RESULT_SUCCESS) {
/* Handle parsing error - Print more debug info */
RETURN_IF_ERROR (r_strbuf_appendf (&disassembler->output,
RETURN_IF_ERROR (_hbc_sb_appendf (&disassembler->output,
"[Error parsing bytecode for function #%u: %s - Offset: %u, Size: %u]\n",
function_id,
result.error_message[0] != '\0'? result.error_message: "Unknown error",
function_header->offset,
function_header->bytecodeSizeInBytes));
function_id, result.error_message[0] != '\0'? result.error_message: "Unknown error",
function_header->offset, function_header->bytecodeSizeInBytes));
/* Skip this function but continue with others */
return SUCCESS_RESULT ();
} else {
/* Print raw bytecode if requested */
if (disassembler->options.show_bytecode) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "Raw bytecode: "));
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "Raw bytecode: "));
for (u32 i = 0; i < function_header->bytecodeSizeInBytes; i++) {
RETURN_IF_ERROR (r_strbuf_appendf (&disassembler->output, "%02x ", function_header->bytecode[i]));
RETURN_IF_ERROR (_hbc_sb_appendf (&disassembler->output, "%02x ", function_header->bytecode[i]));
/* Line break every 16 bytes */
if ((i + 1) % 16 == 0 && i + 1 < function_header->bytecodeSizeInBytes) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "\n "));
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "\n "));
}
}
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "\n\n"));
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "\n\n"));
}
/* Print instructions */
@@ -474,8 +476,8 @@ Result _hbc_disassemble_function(Disassembler *disassembler, u32 function_id) {
}
/* End the function disassembly */
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "\n\n"));
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "===============\n\n"));
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "\n\n"));
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "===============\n\n"));
return SUCCESS_RESULT ();
}
@@ -525,7 +527,7 @@ Result _hbc_output_disassembly(Disassembler *disassembler, const char *output_fi
}
/* Write the output */
fputs (R_STRBUF_SAFEGET (&disassembler->output), out);
fputs (disassembler->output.data, out);
/* Close the file if we opened it */
if (output_file) {
@@ -544,10 +546,13 @@ Result _hbc_disassemble_file(const char *input_file, const char *output_file, HB
/* Initialize HBC reader */
HBCReader reader;
RETURN_IF_ERROR (_hbc_reader_init (&reader));
Result result = _hbc_reader_init (&reader);
if (result.code != RESULT_SUCCESS) {
return result;
}
/* Read the whole file */
Result result = _hbc_reader_read_whole_file (&reader, input_file);
result = _hbc_reader_read_whole_file (&reader, input_file);
if (result.code != RESULT_SUCCESS) {
_hbc_reader_cleanup (&reader);
return result;
@@ -587,16 +592,20 @@ Result _hbc_disassemble_buffer(const u8 *buffer, size_t size, const char *output
/* Initialize HBC reader */
HBCReader reader;
RETURN_IF_ERROR (_hbc_reader_init (&reader));
Result result = _hbc_reader_init (&reader);
if (result.code != RESULT_SUCCESS) {
return result;
}
reader.file_buffer = r_buf_new_with_bytes (buffer, size);
if (!reader.file_buffer) {
/* Initialize buffer reader */
result = _hbc_buffer_reader_init_from_memory (&reader.file_buffer, buffer, size);
if (result.code != RESULT_SUCCESS) {
_hbc_reader_cleanup (&reader);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate buffer");
return result;
}
/* Read header */
Result result = _hbc_reader_read_header (&reader);
result = _hbc_reader_read_header (&reader);
if (result.code != RESULT_SUCCESS) {
_hbc_reader_cleanup (&reader);
return result;
+1 -1
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/hermes_encoder.h>
#include <hbc/opcodes.h>
+1 -4
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -32,7 +32,6 @@
#include "v95.inc.c"
#include "v96.inc.c"
#include "v97.inc.c"
#include "v98.inc.c"
#include "v99.inc.c"
#define ISA_COUNT(v) (sizeof (k_instructions_v ## v) / sizeof (k_instructions_v ## v[0]))
@@ -65,7 +64,6 @@ ISA_ENTRY(94, 256)
ISA_ENTRY(95, 256)
ISA_ENTRY(96, 208)
ISA_ENTRY(97, ISA_COUNT(97))
ISA_ENTRY(98, ISA_COUNT(98))
ISA_ENTRY(99, ISA_COUNT(99))
typedef struct {
@@ -101,7 +99,6 @@ static const IsaVersion k_isa_versions[] = {
{ 95, &k_isa_v95 },
{ 96, &k_isa_v96 },
{ 97, &k_isa_v97 },
{ 98, &k_isa_v98 },
{ 99, &k_isa_v99 },
};
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+10 -10
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[77] = { "call_direct", { { R8, 0 }, { U8, 0 }, { U16, 0 }, { 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, 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 },
[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] = { "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, 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 },
[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 },
[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, 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 },
[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 },
[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 },
+4 -4
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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] = { "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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+14 -14
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[78] = { "call_direct", { { R8, 0 }, { U8, 0 }, { U16, 0 }, { 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, 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 },
[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 },
[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, 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 },
[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 },
[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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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 },
-227
View File
@@ -1,227 +0,0 @@
/* r2hermes - BSD - Copyright 2026 - Ahmeth4n */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
/* Generated from Hermes bytecode version 98 (BytecodeList.def) */
static const Instruction k_instructions_v98[219] = {
[0] = { "unreachable", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[1] = { "new_object_with_buffer", { { R8, 0 }, { U16, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[2] = { "new_object_with_buffer_long", { { R8, 0 }, { U32, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 10 },
[3] = { "new_object_with_buffer_and_parent", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 } }, 11 },
[4] = { "new_object", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[5] = { "new_object_with_parent", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[6] = { "new_array_with_buffer", { { R8, 0 }, { U16, 0 }, { U16, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 } }, 8 },
[7] = { "new_array_with_buffer_long", { { R8, 0 }, { U16, 0 }, { U16, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 } }, 10 },
[8] = { "new_array", { { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[9] = { "new_fast_array", { { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[10] = { "fast_array_length", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[11] = { "fast_array_load", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[12] = { "fast_array_store", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[13] = { "fast_array_push", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[14] = { "fast_array_append", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[15] = { "cache_new_object", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 8 },
[16] = { "mov", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[17] = { "mov_long", { { R32, 0 }, { R32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 9 },
[18] = { "negate", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[19] = { "not", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[20] = { "bit_not", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[21] = { "type_of", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[22] = { "eq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[23] = { "strict_eq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[24] = { "neq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[25] = { "strict_neq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[26] = { "less", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[27] = { "less_eq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[28] = { "greater", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[29] = { "greater_eq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[30] = { "add", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[31] = { "add_n", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[32] = { "add_s", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[33] = { "mul", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[34] = { "mul_n", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[35] = { "div", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[36] = { "div_n", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[37] = { "mod", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[38] = { "sub", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[39] = { "sub_n", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[40] = { "l_shift", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[41] = { "r_shift", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[42] = { "u_rshift", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[43] = { "bit_and", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[44] = { "bit_xor", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[45] = { "bit_or", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[46] = { "inc", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[47] = { "dec", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[48] = { "instance_of", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[49] = { "is_in", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[50] = { "private_is_in", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[51] = { "type_of_is", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[52] = { "get_parent_environment", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[53] = { "get_environment", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[54] = { "get_closure_environment", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[55] = { "store_to_environment", { { R8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[56] = { "store_to_environment_l", { { R8, 0 }, { U16, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[57] = { "store_np_to_environment", { { R8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[58] = { "store_np_to_environment_l", { { R8, 0 }, { U16, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[59] = { "load_from_environment", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[60] = { "load_from_environment_l", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[61] = { "get_global_object", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[62] = { "get_new_target", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[63] = { "load_parent_no_traps", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[64] = { "create_function_environment", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[65] = { "create_top_level_environment", { { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[66] = { "create_environment", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[67] = { "declare_global_var", { { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[68] = { "get_by_id_short", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U8, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 5 },
[69] = { "get_by_id", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[70] = { "get_by_id_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[71] = { "get_by_id_with_receiver_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 } }, 9 },
[72] = { "try_get_by_id", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[73] = { "try_get_by_id_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[74] = { "put_by_id_loose", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[75] = { "put_by_id_strict", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[76] = { "put_by_id_loose_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[77] = { "put_by_id_strict_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[78] = { "try_put_by_id_loose", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[79] = { "try_put_by_id_strict", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[80] = { "try_put_by_id_loose_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[81] = { "try_put_by_id_strict_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[82] = { "put_own_by_slot_idx", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "put_own_by_slot_idx_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "get_own_by_slot_idx", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "get_own_by_slot_idx_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "define_own_by_id", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[87] = { "define_own_by_id_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[88] = { "define_own_by_index", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[89] = { "define_own_by_index_l", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[90] = { "define_own_in_dense_array", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[91] = { "define_own_in_dense_array_l", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[92] = { "define_own_by_val", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[93] = { "get_by_val", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[94] = { "get_by_index", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[95] = { "get_by_val_with_receiver", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[96] = { "put_by_val_loose", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[97] = { "put_by_val_strict", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[98] = { "put_by_val_with_receiver", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 } }, 6 },
[99] = { "del_by_val", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[100] = { "add_own_private_by_sym", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[101] = { "get_own_private_by_sym", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[102] = { "put_own_private_by_sym", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[103] = { "define_own_getter_setter_by_val", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 } }, 6 },
[104] = { "get_p_name_list", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[105] = { "get_next_p_name", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 } }, 6 },
[106] = { "call", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[107] = { "construct", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[108] = { "call1", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[109] = { "call_with_new_target", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[110] = { "call2", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[111] = { "call3", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 } }, 6 },
[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, 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 },
[121] = { "throw", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[122] = { "throw_if_empty", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[123] = { "throw_if_undefined", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[124] = { "throw_if_this_initialized", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[125] = { "debugger", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[126] = { "async_break_check", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[127] = { "profile_point", { { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[128] = { "create_base_class", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[129] = { "create_base_class_long_index", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 } }, 8 },
[130] = { "create_derived_class", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 } }, 7 },
[131] = { "create_derived_class_long_index", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 } }, 9 },
[132] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[133] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[134] = { "create_this_for_new", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[135] = { "create_this_for_super", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[136] = { "select_object", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[137] = { "load_param", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[138] = { "load_param_long", { { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[139] = { "load_const_u_int8", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[140] = { "load_const_int", { { R8, 0 }, { I32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[141] = { "load_const_double", { { R8, 0 }, { DBL, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 10 },
[142] = { "load_const_big_int", { { R8, 0 }, { U16, OPERAND_MEANING_BIGINT_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[143] = { "load_const_big_int_long_index", { { R8, 0 }, { U32, OPERAND_MEANING_BIGINT_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[144] = { "load_const_string", { { R8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[145] = { "load_const_string_long_index", { { R8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[146] = { "load_const_empty", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[147] = { "load_const_undefined", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[148] = { "load_const_null", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[149] = { "load_const_true", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[150] = { "load_const_false", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[151] = { "load_const_zero", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[152] = { "coerce_this_ns", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[153] = { "load_this_ns", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[154] = { "to_number", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[155] = { "to_numeric", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[156] = { "to_int32", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[157] = { "add_empty_string", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[158] = { "create_private_name", { { R8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[159] = { "get_arguments_prop_by_val_loose", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[160] = { "get_arguments_prop_by_val_strict", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[161] = { "get_arguments_length", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[162] = { "reify_arguments_strict", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[163] = { "reify_arguments_loose", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[164] = { "to_property_key", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[165] = { "create_reg_exp", { { R8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { U32, OPERAND_MEANING_STRING_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 } }, 14 },
[166] = { "u_int_switch_imm", { { R8, 0 }, { U32, 0 }, { A32, 0 }, { U32, 0 }, { U32, 0 }, { 0, 0 } }, 18 },
[167] = { "string_switch_imm", { { R8, 0 }, { U32, 0 }, { U32, 0 }, { A32, 0 }, { U32, 0 }, { 0, 0 } }, 18 },
[168] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[169] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[170] = { "iterator_begin", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[171] = { "iterator_next", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[172] = { "iterator_close", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[173] = { "typed_load_parent", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[174] = { "jmp", { { A8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[175] = { "jmp_long", { { A32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[176] = { "jmp_true", { { A8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[177] = { "jmp_true_long", { { A32, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[178] = { "jmp_false", { { A8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[179] = { "jmp_false_long", { { A32, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[180] = { "jmp_undefined", { { A8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[181] = { "jmp_undefined_long", { { A32, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[182] = { "jmp_type_of_is", { { A32, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 8 },
[183] = { "j_less", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[184] = { "j_less_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[185] = { "j_not_less", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[186] = { "j_not_less_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[187] = { "j_less_n", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[188] = { "j_less_n_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[189] = { "j_not_less_n", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[190] = { "j_not_less_n_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[191] = { "j_less_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[192] = { "j_less_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[193] = { "j_not_less_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[194] = { "j_not_less_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[195] = { "j_less_equal_n", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[196] = { "j_less_equal_n_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[197] = { "j_not_less_equal_n", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[198] = { "j_not_less_equal_n_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[199] = { "j_greater", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[200] = { "j_greater_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[201] = { "j_not_greater", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[202] = { "j_not_greater_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[203] = { "j_greater_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[204] = { "j_greater_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[205] = { "j_not_greater_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[206] = { "j_not_greater_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[207] = { "j_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[208] = { "j_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[209] = { "j_not_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[210] = { "j_not_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[211] = { "j_strict_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[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, 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 },
};
+8 -8
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -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, 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 },
[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 },
[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, 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 },
[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 },
};
+123 -73
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/bytecode.h>
#include <hbc/opcodes.h>
@@ -120,25 +120,29 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
/* Read directly from the function bytecode: the parser never writes and
* the header-owned buffer outlives the parse, so no owning copy is needed */
BufferReader bytecode_buffer = { function_header->bytecode, function_header->bytecodeSizeInBytes, 0 };
Result result;
RBuffer *bytecode_buffer = r_buf_new_with_pointers (function_header->bytecode, function_header->bytecodeSizeInBytes, false);
if (!bytecode_buffer) {
_hbc_parsed_instruction_list_free (out_instructions);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate bytecode buffer view");
}
/* Parse instructions in a loop similar to Python's implementation */
while (r_buf_tell (bytecode_buffer) < r_buf_size (bytecode_buffer)) {
while (bytecode_buffer.position < bytecode_buffer.size) {
/* Remember the original position */
size_t original_pos = r_buf_tell (bytecode_buffer);
size_t original_pos = bytecode_buffer.position;
/* Make sure we have at least one byte to read */
if (r_buf_tell (bytecode_buffer) >= r_buf_size (bytecode_buffer)) {
if (bytecode_buffer.position >= bytecode_buffer.size) {
break;
}
/* Read opcode */
u8 opcode = r_buf_read8 (bytecode_buffer);
u8 opcode;
result = _hbc_buffer_reader_read_u8 (&bytecode_buffer, &opcode);
if (result.code != RESULT_SUCCESS) {
hbc_debug_printf ("Error reading opcode at position %zu: %s\n",
original_pos,
result.error_message);
_hbc_parsed_instruction_list_free (out_instructions);
return result;
}
/* Find instruction definition */
const Instruction *inst = NULL;
@@ -149,7 +153,7 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
}
}
if (!inst) {
size_t remaining = r_buf_size (bytecode_buffer) > original_pos? (r_buf_size (bytecode_buffer) - original_pos): 0;
size_t remaining = bytecode_buffer.size > original_pos? (bytecode_buffer.size - original_pos): 0;
if (remaining == 0) {
break;
}
@@ -170,15 +174,10 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
instruction.hbc_reader = reader;
instruction.switch_jump_table = NULL;
instruction.switch_jump_table_size = 0;
result = _hbc_parsed_instruction_list_add (out_instructions, &instruction);
if (result.code != RESULT_SUCCESS) {
_hbc_parsed_instruction_list_free (out_instructions);
r_unref (bytecode_buffer);
return result;
}
RETURN_IF_ERROR (_hbc_parsed_instruction_list_add (out_instructions, &instruction));
/* Stop parsing this function and move on. */
r_buf_seek (bytecode_buffer, r_buf_size (bytecode_buffer), R_BUF_SET);
bytecode_buffer.position = bytecode_buffer.size;
break;
}
@@ -218,9 +217,7 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
case OPERAND_TYPE_DOUBLE: need = 8; break;
default: need = 0; break;
}
ut64 pos = r_buf_tell (bytecode_buffer);
ut64 size = r_buf_size (bytecode_buffer);
if (need && (pos > size || need > size - pos)) {
if (need && (bytecode_buffer.position > bytecode_buffer.size || need > bytecode_buffer.size - bytecode_buffer.position)) {
parsing_failed = true;
break;
}
@@ -228,11 +225,24 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
switch (operand_type) {
case OPERAND_TYPE_REG8:
case OPERAND_TYPE_UINT8:
operand_values[i] = r_buf_read8 (bytecode_buffer);
break;
{
u8 value;
Result read_result = _hbc_buffer_reader_read_u8 (&bytecode_buffer, &value);
if (read_result.code != RESULT_SUCCESS) {
parsing_failed = true;
break;
}
operand_values[i] = value;
break;
}
case OPERAND_TYPE_ADDR8:
{
u8 value_u = r_buf_read8 (bytecode_buffer);
u8 value_u;
Result read_result = _hbc_buffer_reader_read_u8 (&bytecode_buffer, &value_u);
if (read_result.code != RESULT_SUCCESS) {
parsing_failed = true;
break;
}
/* Sign-extend relative offsets */
i8 value_s = (i8)value_u;
operand_values[i] = (u32) (i32)value_s;
@@ -240,17 +250,38 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
}
case OPERAND_TYPE_UINT16:
operand_values[i] = r_buf_read_le16 (bytecode_buffer);
break;
{
u16 value;
Result read_result = _hbc_buffer_reader_read_u16 (&bytecode_buffer, &value);
if (read_result.code != RESULT_SUCCESS) {
parsing_failed = true;
break;
}
operand_values[i] = value;
break;
}
case OPERAND_TYPE_REG32:
case OPERAND_TYPE_UINT32:
case OPERAND_TYPE_IMM32:
operand_values[i] = r_buf_read_le32 (bytecode_buffer);
break;
{
u32 value;
Result read_result = _hbc_buffer_reader_read_u32 (&bytecode_buffer, &value);
if (read_result.code != RESULT_SUCCESS) {
parsing_failed = true;
break;
}
operand_values[i] = value;
break;
}
case OPERAND_TYPE_ADDR32:
{
u32 value_u = r_buf_read_le32 (bytecode_buffer);
u32 value_u;
Result read_result = _hbc_buffer_reader_read_u32 (&bytecode_buffer, &value_u);
if (read_result.code != RESULT_SUCCESS) {
parsing_failed = true;
break;
}
/* Sign-extend relative offsets */
i32 value_s = (i32)value_u;
operand_values[i] = (u32)value_s;
@@ -259,8 +290,11 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
case OPERAND_TYPE_DOUBLE:
{
double value;
u64 raw = r_buf_read_le64 (bytecode_buffer);
memcpy (&value, &raw, sizeof (value));
Result read_result = _hbc_buffer_reader_read_u64 (&bytecode_buffer, (u64 *)&value);
if (read_result.code != RESULT_SUCCESS) {
parsing_failed = true;
break;
}
has_double_arg2 = true;
double_arg2_value = value;
break;
@@ -320,14 +354,13 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
instruction.switch_jump_table = (u32 *)malloc (jump_table_size * sizeof (u32));
if (!instruction.switch_jump_table) {
_hbc_parsed_instruction_list_free (out_instructions);
r_unref (bytecode_buffer);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate jump table");
}
instruction.switch_jump_table_size = jump_table_size;
/* Resolve jump table base pointer. The Python impl uses func_off + instr_off + arg2 first. */
u32 base = instruction.arg2;
size_t fsz = r_buf_size (reader->file_buffer);
size_t fsz = reader->file_buffer.size;
u32 candidates[3];
candidates[0] = function_header->offset + (u32)original_pos + base; /* instruction-relative */
candidates[1] = function_header->offset + base; /* function-relative */
@@ -341,6 +374,8 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
}
}
/* Save file position */
size_t saved_pos = reader->file_buffer.position;
bool read_ok = false;
for (int ci = 0; ci < 3 && !read_ok; ci++) {
@@ -349,17 +384,26 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
continue;
}
ut64 table_pos = R_ROUND (jt_off, 4);
if (table_pos > fsz) {
/* Seek and align to 4 bytes */
Result seek_result = _hbc_buffer_reader_seek (&reader->file_buffer, jt_off);
if (seek_result.code != RESULT_SUCCESS) {
continue;
}
ut64 table_bytes = (ut64)jump_table_size * sizeof (u32);
if (table_bytes > fsz - table_pos) {
size_t rem = reader->file_buffer.position % 4;
if (rem != 0) {
if (reader->file_buffer.position + (4 - rem) > fsz) {
continue;
}
_hbc_buffer_reader_seek (&reader->file_buffer, reader->file_buffer.position + (4 - rem));
}
/* Validate that the whole table fits */
size_t table_end = reader->file_buffer.position + (size_t)jump_table_size * sizeof (u32);
if (table_end > fsz) {
continue;
}
ut64 table_end = table_pos + table_bytes;
/* Jump tables live after the bytecode body, before the next function. */
if (! (table_pos >= func_start && table_end <= table_limit)) {
if (! (reader->file_buffer.position >= func_start && table_end <= table_limit)) {
continue;
}
@@ -369,13 +413,24 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
}
/* Read entries */
bool fail = false;
for (u32 i = 0; i < jump_table_size; i++) {
u32 rel = r_buf_read_le32_at (reader->file_buffer, table_pos + (ut64)i * sizeof (u32));
u32 rel;
Result rr = _hbc_buffer_reader_read_u32 (&reader->file_buffer, &rel);
if (rr.code != RESULT_SUCCESS) {
fail = true;
break;
}
instruction.switch_jump_table[i] = (u32)original_pos + rel;
}
read_ok = true;
if (!fail) {
read_ok = true;
}
}
/* Restore file position */
_hbc_buffer_reader_seek (&reader->file_buffer, saved_pos);
if (!read_ok) {
/* Fill with safe defaults */
for (u32 i = 0; i < jump_table_size; i++) {
@@ -388,12 +443,10 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
result = _hbc_parsed_instruction_list_add (out_instructions, &instruction);
if (result.code != RESULT_SUCCESS) {
_hbc_parsed_instruction_list_free (out_instructions);
r_unref (bytecode_buffer);
return result;
}
}
r_unref (bytecode_buffer);
return SUCCESS_RESULT ();
}
@@ -430,17 +483,17 @@ u32 _hbc_compute_target_address(const ParsedInstruction *insn, int op_index) {
}
/* Convert instruction to string */
Result _hbc_instruction_to_string(ParsedInstruction *instruction, RStrBuf *out_string) {
Result _hbc_instruction_to_string(ParsedInstruction *instruction, StringBuffer *out_string) {
if (!instruction || !out_string || !instruction->inst) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT,
"Invalid arguments for _hbc_instruction_to_string");
}
/* Add address */
RETURN_IF_ERROR (r_strbuf_appendf (out_string, "%08x", instruction->function_offset + instruction->original_pos));
RETURN_IF_ERROR (r_strbuf_append (out_string, ": <"));
RETURN_IF_ERROR (r_strbuf_append (out_string, instruction->inst->name));
RETURN_IF_ERROR (r_strbuf_append (out_string, ">: <"));
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, "%08x", instruction->function_offset + instruction->original_pos));
RETURN_IF_ERROR (_hbc_sb_append (out_string, ": <"));
RETURN_IF_ERROR (_hbc_sb_append (out_string, instruction->inst->name));
RETURN_IF_ERROR (_hbc_sb_append (out_string, ">: <"));
/* Get operands */
bool first = true;
@@ -450,7 +503,7 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, RStrBuf *out_s
}
if (!first) {
RETURN_IF_ERROR (r_strbuf_append (out_string, ", "));
RETURN_IF_ERROR (_hbc_sb_append (out_string, ", "));
}
first = false;
@@ -461,15 +514,15 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, RStrBuf *out_s
const char *operand_name = hbc_operand_name (&instruction->inst->operands[i]);
/* Print operand name and value */
RETURN_IF_ERROR (r_strbuf_append (out_string, operand_name));
RETURN_IF_ERROR (r_strbuf_append (out_string, ": "));
RETURN_IF_ERROR (_hbc_sb_append (out_string, operand_name));
RETURN_IF_ERROR (_hbc_sb_append (out_string, ": "));
/* Format value based on operand type */
RETURN_IF_ERROR (r_strbuf_appendf (out_string, "%u", value));
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, "%u", value));
}
/* Close operands bracket */
RETURN_IF_ERROR (r_strbuf_append (out_string, ">"));
RETURN_IF_ERROR (_hbc_sb_append (out_string, ">"));
/* Add comments for special operands */
HBCReader *reader = instruction->hbc_reader;
@@ -485,18 +538,18 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, RStrBuf *out_s
switch (operand_meaning) {
case OPERAND_MEANING_STRING_ID:
if (value < reader->header.stringCount) {
RETURN_IF_ERROR (r_strbuf_append (out_string, " # String: \""));
RETURN_IF_ERROR (r_strbuf_append (out_string, reader->strings[value]));
RETURN_IF_ERROR (r_strbuf_append (out_string, "\" ("));
RETURN_IF_ERROR (r_strbuf_append (out_string, _hbc_string_kind_to_string (reader->string_kinds[value])));
RETURN_IF_ERROR (r_strbuf_append (out_string, ")"));
RETURN_IF_ERROR (_hbc_sb_append (out_string, " # String: \""));
RETURN_IF_ERROR (_hbc_sb_append (out_string, reader->strings[value]));
RETURN_IF_ERROR (_hbc_sb_append (out_string, "\" ("));
RETURN_IF_ERROR (_hbc_sb_append (out_string, _hbc_string_kind_to_string (reader->string_kinds[value])));
RETURN_IF_ERROR (_hbc_sb_append (out_string, ")"));
}
break;
case OPERAND_MEANING_BIGINT_ID:
if (value < reader->bigint_count) {
RETURN_IF_ERROR (r_strbuf_append (out_string, " # BigInt: "));
RETURN_IF_ERROR (r_strbuf_appendf (out_string, "%lld", (long long)reader->bigint_values[value]));
RETURN_IF_ERROR (_hbc_sb_append (out_string, " # BigInt: "));
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, "%lld", (long long)reader->bigint_values[value]));
}
break;
@@ -508,20 +561,17 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, RStrBuf *out_s
func_name = reader->strings[func->functionName];
}
RETURN_IF_ERROR (r_strbuf_appendf (out_string,
RETURN_IF_ERROR (_hbc_sb_appendf (out_string,
" # Function: [#%u %s of %u bytes]: %u params @ offset 0x%08x",
value,
func_name,
func->bytecodeSizeInBytes,
func->paramCount,
func->offset));
value, func_name, func->bytecodeSizeInBytes,
func->paramCount, func->offset));
}
break;
case OPERAND_MEANING_BUILTIN_ID:
/* Add support for builtin functions when available */
RETURN_IF_ERROR (r_strbuf_append (out_string, " # Built-in function: "));
RETURN_IF_ERROR (r_strbuf_appendf (out_string, "#%u", value));
RETURN_IF_ERROR (_hbc_sb_append (out_string, " # Built-in function: "));
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, "#%u", value));
break;
default:
@@ -541,24 +591,24 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, RStrBuf *out_s
/* For addresses, calculate absolute target address */
u32 absolute_address = instruction->function_offset + instruction->original_pos + value;
RETURN_IF_ERROR (r_strbuf_appendf (out_string, " # Address: %08x", absolute_address));
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, " # Address: %08x", absolute_address));
}
/* Add jump table for switch instructions */
if (instruction->opcode == OP_SwitchImm &&
instruction->switch_jump_table && instruction->switch_jump_table_size > 0) {
RETURN_IF_ERROR (r_strbuf_append (out_string, " # Jump table: ["));
RETURN_IF_ERROR (_hbc_sb_append (out_string, " # Jump table: ["));
for (u32 i = 0; i < instruction->switch_jump_table_size; i++) {
if (i > 0) {
RETURN_IF_ERROR (r_strbuf_append (out_string, ", "));
RETURN_IF_ERROR (_hbc_sb_append (out_string, ", "));
}
RETURN_IF_ERROR (r_strbuf_appendf (out_string, "%08x", instruction->function_offset + instruction->switch_jump_table[i]));
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, "%08x", instruction->function_offset + instruction->switch_jump_table[i]));
}
RETURN_IF_ERROR (r_strbuf_append (out_string, "]"));
RETURN_IF_ERROR (_hbc_sb_append (out_string, "]"));
}
}
File diff suppressed because it is too large. Load diff
+391 -61
View File
@@ -1,6 +1,7 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/hbc.h>
#include <hbc/parser.h>
#include <ctype.h>
static void sanitize_identifier(const char *src, char *dst, size_t dst_size) {
@@ -12,103 +13,432 @@ static void sanitize_identifier(const char *src, char *dst, size_t dst_size) {
dst[out] = '\0';
}
static void string_flag_name(const char *str, u32 index, char *dst, size_t dst_size) {
char preview[64] = { 0 };
size_t out = 0;
if (str) {
for (size_t i = 0; str[i] && i < 32 && out + 1 < sizeof (preview); i++) {
unsigned char c = (unsigned char)str[i];
preview[out++] = (c >= 32 && c < 127)? (char)c: '_';
}
}
preview[out] = '\0';
char sanitized[64] = { 0 };
sanitize_identifier (preview, sanitized, sizeof (sanitized));
snprintf (dst, dst_size, "%s_%u", sanitized[0]? sanitized: "str", index);
}
Result _hbc_generate_r2_script(const char *input_file, const char *output_file) {
if (!input_file) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Input file is NULL");
}
HBC *hbc = NULL;
Result result = hbc_open (input_file, &hbc);
/* Initialize HBC reader */
HBCReader reader;
Result result = _hbc_reader_init (&reader);
if (result.code != RESULT_SUCCESS) {
fprintf (stderr, "Error reading file: %s\n", result.error_message);
return result;
}
/* Read and parse the file */
result = _hbc_reader_read_file (&reader, input_file);
if (result.code != RESULT_SUCCESS) {
fprintf (stderr, "Error reading file: %s\n", result.error_message);
_hbc_reader_cleanup (&reader);
return result;
}
/* Parse header and function info */
result = _hbc_reader_read_header (&reader);
if (result.code != RESULT_SUCCESS) {
fprintf (stderr, "Error reading header: %s\n", result.error_message);
_hbc_reader_cleanup (&reader);
return result;
}
/* Open output file early so we can at least output basic info even if function parsing fails */
FILE *out = stdout;
if (output_file) {
out = fopen (output_file, "w");
if (!out) {
hbc_close (hbc);
fprintf (stderr, "Error opening output file: %s\n", output_file);
_hbc_reader_cleanup (&reader);
return ERROR_RESULT (RESULT_ERROR_FILE_NOT_FOUND, "Failed to open output file");
}
}
HBCHeader header = { 0 };
result = hbc_get_header (hbc, &header);
if (result.code != RESULT_SUCCESS) {
if (output_file) {
fclose (out);
}
hbc_close (hbc);
return result;
}
/* Generate r2 script header */
fprintf (out, "# R2 script for Hermes bytecode file: %s\n", input_file);
fprintf (out, "# Generated by r2hermes\n\n");
fprintf (out, "# Hermes File Version: %u\n", header.version);
fprintf (out, "# File Size: %u bytes\n", header.fileLength);
fprintf (out, "# Function Count: %u\n\n", header.functionCount);
/* Add basic file info as comments */
fprintf (out, "# Hermes File Version: %u\n", reader.header.version);
fprintf (out, "# File Size: %u bytes\n", reader.header.fileLength);
fprintf (out, "# Function Count: %u\n\n", reader.header.functionCount);
/* Create flag for HBC header */
fprintf (out, "# Basic file structure flags\n");
fprintf (out, "f hbc.header=0x0\n");
fprintf (out, "f hbc.header.size=0x%lx\n", (unsigned long)sizeof (HBCHeader));
const u32 function_count = R_MIN (hbc_function_count (hbc), 50000);
fprintf (out, "\n# Function flags\n");
/* ================= ROBUST FUNCTION PARSING ================= */
/* Align buffer */
result = _hbc_buffer_reader_align (&reader.file_buffer, 4);
if (result.code != RESULT_SUCCESS) {
fprintf (stderr, "Warning: Error aligning buffer for functions: %s\n", result.error_message);
fprintf (out, "# Warning: Error aligning buffer for functions: %s\n", result.error_message);
_hbc_reader_cleanup (&reader);
return SUCCESS_RESULT (); /* Return success since we've written what we can */
}
/* Set reasonable limits for function count */
const u32 MAX_SAFE_FUNCTIONS = 50000;
u32 function_count = reader.header.functionCount;
if (function_count > MAX_SAFE_FUNCTIONS) {
fprintf (stderr, "Warning: Very large function count (%u). Limiting to %u for safety.\n", reader.header.functionCount, MAX_SAFE_FUNCTIONS);
function_count = MAX_SAFE_FUNCTIONS;
}
fprintf (stderr, "Reading functions at position %zu of %zu bytes.\n", reader.file_buffer.position, reader.file_buffer.size);
fprintf (out, "# Reading %u functions from position 0x%zx\n", function_count, reader.file_buffer.position);
/* Check for reasonable function count vs file size */
size_t min_bytes_needed = function_count * 16; /* Each header is at least 16 bytes */
if (reader.file_buffer.position + min_bytes_needed > reader.file_buffer.size) {
fprintf (stderr, "Warning: File might be truncated. Need ~%zu more bytes for function headers.\n", min_bytes_needed);
fprintf (out, "# Warning: File appears too small for %u functions, may only read partial data\n", function_count);
}
/* Create temporary array for function names to solve temp_name lifetime issues */
char **function_names = (char **)calloc (function_count, sizeof (char *));
if (!function_names) {
fprintf (stderr, "Error: Failed to allocate memory for function names\n");
if (output_file) {
fclose (out);
}
_hbc_reader_cleanup (&reader);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate function names");
}
/* Initialize all function names to NULL */
for (u32 i = 0; i < function_count; i++) {
HBCFunc func = { 0 };
if (hbc_get_function_info (hbc, i, &func).code != RESULT_SUCCESS) {
function_names[i] = NULL;
}
/* Track functions we read successfully */
size_t successful_functions = 0;
u32 *function_offsets = (u32 *)calloc (function_count, sizeof (u32));
u32 *function_sizes = (u32 *)calloc (function_count, sizeof (u32));
if (!function_offsets || !function_sizes) {
fprintf (stderr, "Error: Failed to allocate memory for function offsets or sizes\n");
free (function_names);
free (function_offsets);
free (function_sizes);
if (output_file) {
fclose (out);
}
_hbc_reader_cleanup (&reader);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate memory");
}
/* Print current position in case we have issues */
fprintf (stderr, "Function section start position: %zu\n", reader.file_buffer.position);
/* Read all function headers directly from the buffer */
for (u32 i = 0; i < function_count; i++) {
/* Safety check - ensure we have enough buffer for a function header (16 bytes) */
if (reader.file_buffer.position + 16 > reader.file_buffer.size) {
fprintf (stderr, "Reached end of file after reading %zu of %u functions\n", successful_functions, function_count);
break; /* End reading if we reach end of buffer */
}
/* Track position in case we need to restore it */
size_t start_position = reader.file_buffer.position;
/* Read function header raw data with explicit error handling */
u32 raw_data[4];
bool header_read_failed = false;
for (int j = 0; j < 4; j++) {
Result res = _hbc_buffer_reader_read_u32 (&reader.file_buffer, &raw_data[j]);
if (res.code != RESULT_SUCCESS) {
fprintf (stderr, "Error reading function %u header word %d: %s\n", i, j, res.error_message);
header_read_failed = true;
break;
}
}
if (header_read_failed) {
/* Restore position and break */
_hbc_buffer_reader_seek (&reader.file_buffer, start_position);
break;
}
/* Extract fields from raw data based on the Python implementation */
u32 offset = raw_data[0] & 0x1FFFFFF; /* 25 bits */
/* Unused: u32 paramCount = (raw_data[0] >> 25) & 0x7F; */
u32 bytecodeSizeInBytes = raw_data[1] & 0x7FFF; /* 15 bits */
/* Unused: u32 functionName = (raw_data[1] >> 15) & 0x1FFFF; */
/* Unused:
u32 infoOffset = raw_data[2] & 0x1FFFFFF;
u32 frameSize = (raw_data[2] >> 25) & 0x7F;
u8 flags = (u8) ((raw_data[3] >> 24) & 0xFF);
bool overflowed = (flags >> 5) & 0x1;
*/
/* Skip if offset is invalid or outside file bounds */
if (offset == 0 || offset >= reader.file_buffer.size) {
/* Skip this function silently */
continue;
}
char name[64];
snprintf (name, sizeof (name), "func_%u", i);
if (func.size > 0) {
fprintf (out, "'f func.hermes.%s 0x%x 0x%x\n", name, func.size, func.offset);
/* Store offset and size for flag generation */
function_offsets[successful_functions] = offset;
function_sizes[successful_functions] = bytecodeSizeInBytes;
/* Generate a default function name based on index */
char *temp_name = (char *)malloc (32);
if (temp_name) {
snprintf (temp_name, 32, "func_%u", i);
function_names[successful_functions] = temp_name;
}
successful_functions++;
}
fprintf (stderr, "Successfully read %zu function headers\n", successful_functions);
fprintf (out, "# Successfully read %zu function headers\n\n", successful_functions);
/* ==== READ STRING TABLES AND CREATE STRING FLAGS ==== */
/* Structure to store string info temporarily */
typedef struct {
u32 offset; /* Offset in string storage */
u32 length; /* Length of string */
bool isUTF16; /* Whether the string is UTF-16 encoded */
} StringInfo;
StringInfo *string_infos = NULL;
u8 *string_storage = NULL;
size_t string_storage_size = 0;
/* Output function flags */
fprintf (out, "# Function flags\n");
for (size_t i = 0; i < successful_functions; i++) {
/* Use function names we generated earlier */
const char *function_name = function_names[i]? function_names[i]: "unknown";
/* Generate sanitized name for r2 */
char sanitized_name[256] = { 0 };
sanitize_identifier (function_name, sanitized_name, sizeof (sanitized_name));
/* If sanitized name is empty, use a default */
if (sanitized_name[0] == '\0') {
snprintf (sanitized_name, sizeof (sanitized_name), "func_%zu", i);
}
/* Write the function flag */
/* Add size info if available */
if (function_sizes[i] > 0) {
fprintf (out, "'f func.hermes.%s 0x%x 0x%x\n", sanitized_name, function_sizes[i], function_offsets[i]);
} else {
fprintf (out, "'f func.hermes.%s=0x%x\n", name, func.offset);
fprintf (out, "'f func.hermes.%s=0x%x\n", sanitized_name, function_offsets[i]);
}
}
fprintf (out, "# Successfully read %u function headers\n\n", function_count);
fprintf (out, "# String flags\n");
const u32 string_count = R_MIN (hbc_string_count (hbc), 100000);
u32 successful_strings = 0;
for (u32 i = 0; i < string_count; i++) {
HBCStringMeta meta = { 0 };
if (hbc_get_string_meta (hbc, i, &meta).code != RESULT_SUCCESS) {
continue;
/* Now try to parse string tables based on Python implementation */
fprintf (out, "\n# String flags\n");
bool strings_parsed = false;
/* First try to locate and read the string tables */
/* Reset file position */
if (_hbc_buffer_reader_seek (&reader.file_buffer, sizeof (HBCHeader)).code == RESULT_SUCCESS) {
/* Skip over functions section */
u32 function_headers_bytes = reader.header.functionCount * 16; /* 16 bytes per small header */
result = _hbc_buffer_reader_seek (&reader.file_buffer, reader.file_buffer.position + function_headers_bytes);
if (result.code == RESULT_SUCCESS) {
/* Align for string kinds */
result = _hbc_buffer_reader_align (&reader.file_buffer, 4);
if (result.code == RESULT_SUCCESS) {
/* Skip over string kinds section */
if (reader.header.stringKindCount > 0) {
result = _hbc_buffer_reader_seek (&reader.file_buffer,
reader.file_buffer.position + reader.header.stringKindCount * sizeof (u32));
}
if (result.code == RESULT_SUCCESS) {
/* Skip over identifier hashes section */
if (reader.header.identifierCount > 0) {
result = _hbc_buffer_reader_align (&reader.file_buffer, 4);
if (result.code == RESULT_SUCCESS) {
result = _hbc_buffer_reader_seek (&reader.file_buffer,
reader.file_buffer.position + reader.header.identifierCount * sizeof (u32));
}
}
if (result.code == RESULT_SUCCESS) {
/* Prepare to read string tables */
result = _hbc_buffer_reader_align (&reader.file_buffer, 4);
if (result.code == RESULT_SUCCESS && reader.header.stringCount > 0) {
fprintf (stderr, "Reading string tables at position %zu\n", reader.file_buffer.position);
/* Allocate storage for string info */
u32 safe_string_count = reader.header.stringCount;
if (safe_string_count > 100000) {
safe_string_count = 100000; /* Safety limit */
}
string_infos = (StringInfo *)calloc (safe_string_count, sizeof (StringInfo));
if (!string_infos) {
fprintf (stderr, "Failed to allocate memory for string infos\n");
} else {
/* Read the small string table entries */
bool read_success = true;
for (u32 i = 0; i < safe_string_count; i++) {
u32 entry;
result = _hbc_buffer_reader_read_u32 (&reader.file_buffer, &entry);
if (result.code != RESULT_SUCCESS) {
read_success = false;
break;
}
/* Parse entry fields */
string_infos[i].isUTF16 = entry & 0x1;
string_infos[i].offset = (entry >> 1) & 0x7FFFFF; /* 23 bits */
string_infos[i].length = (entry >> 24) & 0xFF; /* 8 bits */
}
/* Skip overflow string table if present */
if (read_success && reader.header.overflowStringCount > 0) {
result = _hbc_buffer_reader_align (&reader.file_buffer, 4);
if (result.code == RESULT_SUCCESS) {
/* Skip past each overflow entry (8 bytes each) */
result = _hbc_buffer_reader_seek (&reader.file_buffer,
reader.file_buffer.position + reader.header.overflowStringCount * 8);
}
}
/* Now read the actual string data */
if (read_success && result.code == RESULT_SUCCESS) {
/* Align buffer for string storage */
result = _hbc_buffer_reader_align (&reader.file_buffer, 4);
if (result.code == RESULT_SUCCESS && reader.header.stringStorageSize > 0) {
/* Safety check - limit large string storage */
size_t storage_size = reader.header.stringStorageSize;
if (storage_size > 10 * 1024 * 1024) { /* 10MB limit */
storage_size = 10 * 1024 * 1024;
}
/* Make sure we have room to read */
if (reader.file_buffer.position + storage_size <= reader.file_buffer.size) {
/* Allocate space for string storage */
string_storage = (u8 *)malloc (storage_size);
if (string_storage) {
/* Read entire string storage section */
result = _hbc_buffer_reader_read_bytes (&reader.file_buffer, string_storage, storage_size);
if (result.code == RESULT_SUCCESS) {
string_storage_size = storage_size;
strings_parsed = true;
fprintf (stderr, "Successfully read %zu bytes of string data\n", storage_size);
}
}
}
}
}
}
}
}
}
}
}
const char *str = NULL;
(void)hbc_get_string (hbc, i, &str);
char name[96];
string_flag_name (str, i, name, sizeof (name));
const u32 bpc = meta.isUTF16? 2: 1;
fprintf (out, "'f str.%s %u 0x%08x\n", name, meta.length * bpc, meta.offset);
successful_strings++;
}
fprintf (out, "# Generated %u string flags\n", successful_strings);
/* Generate string flags if we managed to read the string data */
if (strings_parsed && string_infos && string_storage) {
u32 safe_string_count = reader.header.stringCount;
if (safe_string_count > 100000) {
safe_string_count = 100000;
}
/* Create flags for each string */
u32 successful_strings = 0;
for (u32 i = 0; i < safe_string_count; i++) {
u32 offset = string_infos[i].offset;
u32 length = string_infos[i].length;
bool isUTF16 = string_infos[i].isUTF16;
/* Overflow-safe bounds check via subtraction */
const u32 bpc = isUTF16? 2: 1;
if (offset > string_storage_size || length > (string_storage_size - offset) / bpc) {
continue;
}
/* For very long strings, truncate for flag name purposes */
u32 display_length = length;
if (display_length > 32) {
display_length = 32;
}
/* Generate a string value for the flag name: decode each char (1 or
* 2 bytes, LE) and keep only ASCII-printable bytes, else '_'. */
char str_value[64] = { 0 };
u32 chars_added = 0;
for (u32 j = 0; j < display_length && chars_added < sizeof (str_value) - 1; j++) {
const u8 *p = string_storage + offset + (j * bpc);
u32 c = isUTF16? ((u32)p[0] | ((u32)p[1] << 8)): p[0];
str_value[chars_added++] = (c >= 32 && c < 127)? (char)c: '_';
}
str_value[chars_added] = '\0';
/* Generate sanitized name for r2 - only include alphanumeric and underscores */
char sanitized_name[64] = { 0 };
sanitize_identifier (str_value, sanitized_name, sizeof (sanitized_name));
/* Add index to name to ensure uniqueness */
char unique_name[96];
snprintf (unique_name, sizeof (unique_name), "%s_%u", sanitized_name[0]? sanitized_name: "str", i);
/* Write the string flag - offset is relative to string storage base */
uint32_t addr = (unsigned long) (reader.file_buffer.position - string_storage_size + offset);
fprintf (out, "'f str.%s %u 0x%08x\n", unique_name, length * bpc, addr);
#if 0
/* Add flag for string length */
if (isUTF16) {
fprintf (out, "f str.%s.size=0x%x\n", unique_name, length * 2);
} else {
fprintf (out, "f str.%s.size=0x%x\n", unique_name, length);
}
#endif
successful_strings++;
}
fprintf (stderr, "Generated flags for %u strings\n", successful_strings);
fprintf (out, "# Generated %u string flags\n", successful_strings);
} else {
fprintf (stderr, "Could not parse strings\n");
fprintf (out, "# Could not parse string data\n");
}
/* Clean up string parsing resources */
if (string_infos) {
free (string_infos);
}
if (string_storage) {
free (string_storage);
}
/* Free function names */
for (u32 i = 0; i < function_count; i++) {
if (function_names[i]) {
free (function_names[i]);
}
}
free (function_names);
free (function_offsets);
free (function_sizes);
/* Close output file if needed */
if (output_file) {
fclose (out);
printf ("\n[+] R2 script output wrote to \"%s\"\n\n", output_file);
}
hbc_close (hbc);
/* Clean up */
_hbc_reader_cleanup (&reader);
return SUCCESS_RESULT ();
}
+172
View File
@@ -0,0 +1,172 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/common.h>
Result _hbc_buffer_reader_init_from_file(BufferReader *reader, const char *filename) {
if (!reader || !filename) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Reader or filename is NULL");
}
FILE *file = fopen (filename, "rb");
if (!file) {
return ERROR_RESULT (RESULT_ERROR_FILE_NOT_FOUND, "Failed to open file");
}
/* Get file size */
fseek (file, 0, SEEK_END);
long file_size = ftell (file);
fseek (file, 0, SEEK_SET);
if (file_size <= 0) {
fclose (file);
return ERROR_RESULT (RESULT_ERROR_INVALID_FORMAT, "Empty or invalid file");
}
/* Allocate buffer */
reader->data = (u8 *)malloc (file_size);
if (!reader->data) {
fclose (file);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate buffer for file");
}
/* Read file contents */
size_t bytes_read = fread (reader->data, 1, file_size, file);
fclose (file);
if (bytes_read != (size_t)file_size) {
free (reader->data);
reader->data = NULL;
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Failed to read entire file");
}
reader->size = file_size;
reader->position = 0;
return SUCCESS_RESULT ();
}
Result _hbc_buffer_reader_init_from_memory(BufferReader *reader, const u8 *data, size_t size) {
if (!reader || !data || size == 0) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for _hbc_buffer_reader_init_from_memory");
}
/* Copy the data to ensure ownership */
reader->data = (u8 *)malloc (size);
if (!reader->data) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate buffer");
}
memcpy (reader->data, data, size);
reader->size = size;
reader->position = 0;
return SUCCESS_RESULT ();
}
/* Shared validity + bounds check for a read of `need` bytes at the cursor. */
static Result buffer_reader_check(const BufferReader *reader, size_t need) {
if (!reader || !reader->data) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid buffer reader");
}
if (reader->position > reader->size || need > reader->size - reader->position) {
hbc_debug_printf ("Warning: Buffer overflow prevented reading %zu bytes at position %zu of %zu\n",
need,
reader->position,
reader->size);
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Buffer overflow");
}
return SUCCESS_RESULT ();
}
/* Read `n` (1..8) little-endian bytes at the cursor, advancing the position.
* *out is zeroed on any failure. */
static Result buffer_reader_read_le(BufferReader *reader, size_t n, u64 *out) {
*out = 0;
RETURN_IF_ERROR (buffer_reader_check (reader, n));
u64 v = 0;
for (size_t i = 0; i < n; i++) {
v |= (u64)reader->data[reader->position + i] << (8 * i);
}
reader->position += n;
*out = v;
return SUCCESS_RESULT ();
}
/* Define the fixed-width little-endian readers; all share buffer_reader_read_le. */
#define HBC_DEFINE_READ(bits) \
Result _hbc_buffer_reader_read_u ## bits (BufferReader *reader, u ## bits *out_value) { \
if (!out_value) { \
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "read out_value is NULL"); \
} \
u64 v; \
Result r = buffer_reader_read_le (reader, sizeof (u ## bits), &v); \
*out_value = (u ## bits)v; \
return r; \
}
HBC_DEFINE_READ(8)
HBC_DEFINE_READ(16)
HBC_DEFINE_READ(32)
HBC_DEFINE_READ(64)
#undef HBC_DEFINE_READ
Result _hbc_buffer_reader_read_bytes(BufferReader *reader, u8 *out_buffer, size_t length) {
if (!out_buffer) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for _hbc_buffer_reader_read_bytes");
}
RETURN_IF_ERROR (buffer_reader_check (reader, length));
memcpy (out_buffer, reader->data + reader->position, length);
reader->position += length;
return SUCCESS_RESULT ();
}
Result _hbc_buffer_reader_seek(BufferReader *reader, size_t position) {
if (!reader) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Reader is NULL");
}
/* Safety check - is the reader data valid? */
if (!reader->data) {
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "BufferReader has no data");
}
/* Validate position */
if (position > reader->size) {
hbc_debug_printf ("Warning: Attempted to seek beyond buffer bounds (pos: %zu, size: %zu)\n",
position,
reader->size);
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Seek position beyond buffer size");
}
reader->position = position;
return SUCCESS_RESULT ();
}
Result _hbc_buffer_reader_align(BufferReader *reader, size_t alignment) {
if (!reader) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Reader is NULL");
}
if (alignment == 0) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Alignment must be non-zero");
}
size_t remainder = reader->position % alignment;
if (remainder != 0) {
size_t padding = alignment - remainder;
if (reader->position > reader->size || padding > reader->size - reader->position) {
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Buffer overflow in align");
}
reader->position += padding;
}
return SUCCESS_RESULT ();
}
void _hbc_buffer_reader_free(BufferReader *reader) {
if (reader && reader->data) {
free (reader->data);
reader->data = NULL;
reader->size = 0;
reader->position = 0;
}
}
+188
View File
@@ -0,0 +1,188 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/common.h>
/* Grow the buffer (doubling) so it can hold at least required_capacity bytes. */
static Result sb_ensure(StringBuffer *buffer, size_t required_capacity) {
if (required_capacity <= buffer->capacity) {
return SUCCESS_RESULT ();
}
size_t new_capacity = buffer->capacity? buffer->capacity: 64;
while (new_capacity < required_capacity) {
size_t doubled = new_capacity * 2;
if (doubled <= new_capacity) { /* overflow: just take what's needed */
new_capacity = required_capacity;
break;
}
new_capacity = doubled;
}
char *new_data = (char *)realloc (buffer->data, new_capacity);
if (!new_data) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to resize string buffer");
}
buffer->data = new_data;
buffer->capacity = new_capacity;
return SUCCESS_RESULT ();
}
Result _hbc_sb_init(StringBuffer *buffer, size_t initial_capacity) {
if (!buffer) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Buffer is NULL");
}
if (initial_capacity == 0) {
initial_capacity = 64; /* Default initial capacity */
}
buffer->data = (char *)malloc (initial_capacity);
if (!buffer->data) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate string buffer");
}
buffer->data[0] = '\0';
buffer->length = 0;
buffer->capacity = initial_capacity;
return SUCCESS_RESULT ();
}
Result _hbc_sb_append(StringBuffer *buffer, const char *str) {
if (!buffer || !str) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Buffer or string is NULL");
}
size_t str_len = strlen (str);
if (str_len == 0) {
return SUCCESS_RESULT ();
}
/* Ensure enough capacity */
RETURN_IF_ERROR (sb_ensure (buffer, buffer->length + str_len + 1));
/* Append the string */
memcpy (buffer->data + buffer->length, str, str_len);
buffer->length += str_len;
buffer->data[buffer->length] = '\0';
return SUCCESS_RESULT ();
}
Result _hbc_sb_appendf(StringBuffer *buffer, const char *fmt, ...) {
if (!buffer || !fmt) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Buffer or format is NULL");
}
char stack[128];
va_list ap, ap2;
va_start (ap, fmt);
va_copy (ap2, ap);
int len = vsnprintf (stack, sizeof (stack), fmt, ap);
va_end (ap);
if (len < 0) {
va_end (ap2);
return ERROR_RESULT (RESULT_ERROR_INVALID_FORMAT, "Failed to format string buffer append");
}
if ((size_t)len < sizeof (stack)) {
va_end (ap2);
return _hbc_sb_append (buffer, stack);
}
char *heap = (char *)malloc ((size_t)len + 1);
if (!heap) {
va_end (ap2);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate formatted string");
}
vsnprintf (heap, (size_t)len + 1, fmt, ap2);
va_end (ap2);
Result result = _hbc_sb_append (buffer, heap);
free (heap);
return result;
}
Result _hbc_sb_append_char(StringBuffer *buffer, char c) {
if (!buffer) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Buffer is NULL");
}
/* Ensure enough capacity (+1 for the char, +1 for null terminator) */
RETURN_IF_ERROR (sb_ensure (buffer, buffer->length + 2));
/* Append the character */
buffer->data[buffer->length] = c;
buffer->length++;
buffer->data[buffer->length] = '\0';
return SUCCESS_RESULT ();
}
Result _hbc_sb_append_int(StringBuffer *buffer, int value) {
if (!buffer) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Buffer is NULL");
}
return _hbc_sb_appendf (buffer, "%d", value);
}
void _hbc_sb_free(StringBuffer *buffer) {
if (buffer && buffer->data) {
free (buffer->data);
buffer->data = NULL;
buffer->length = 0;
buffer->capacity = 0;
}
}
/* Convert CamelCase to snake_case for instruction names */
void hbc_camel_to_snake(const char *camel, char *snake, size_t snake_size) {
if (!camel || !snake || snake_size == 0) {
return;
}
size_t j = 0;
for (size_t i = 0; camel[i] && j < snake_size - 1; i++) {
char c = camel[i];
/* Insert underscore before uppercase letter (except at start) */
if (i > 0 && c >= 'A' && c <= 'Z') {
/* Don't insert underscore if previous char was also uppercase
* and next char is lowercase (e.g., "ID" in "GetByID") */
if (! (camel[i - 1] >= 'A' && camel[i - 1] <= 'Z' &&
camel[i + 1] >= 'a' && camel[i + 1] <= 'z')) {
if (j < snake_size - 1) {
snake[j++] = '_';
}
}
}
/* Convert to lowercase */
if (j < snake_size - 1) {
snake[j++] = (c >= 'A' && c <= 'Z')? (c + 32): c;
}
}
snake[j] = '\0';
}
/* Convert snake_case to CamelCase */
void hbc_snake_to_camel(const char *snake, char *camel, size_t camel_size) {
if (!snake || !camel || camel_size < 2) {
if (camel && camel_size > 0) {
camel[0] = '\0';
}
return;
}
size_t j = 0;
bool cap_next = true;
for (size_t i = 0; snake[i] && j + 1 < camel_size; i++) {
if (snake[i] == '_') {
cap_next = true;
} else {
char c = snake[i];
if (cap_next && c >= 'a' && c <= 'z') {
c = c - 32; /* to uppercase */
}
camel[j++] = c;
cap_next = false;
}
}
camel[j] = '\0';
}
+4 -7
View File
@@ -6,18 +6,15 @@ CC ?= gcc
# Enable POSIX functions like strdup with proper prototypes
CFLAGS += -Wall -Wextra -std=c11 -O2 -fPIC -fvisibility=hidden -D_POSIX_C_SOURCE=200809L
CFLAGS += -I../../include
R2_SRC_CFLAGS = $(if $(wildcard ../../../../../include/r_core.h),-I../../../../../include)
CFLAGS += $(shell pkg-config --cflags r_core 2>/dev/null || r2pm -H R2_CFLAGS 2>/dev/null || echo "$(R2_SRC_CFLAGS)")
CFLAGS += $(shell pkg-config --cflags r_core 2>/dev/null || echo "-I/usr/local/include/libr")
R2_PLUGDIR?=$(shell r2 -H R2_USER_PLUGINS)
R2_PREFIX?=$(shell r2 -H R2_PREFIX)
R2PM_LINK_FLAGS := $(strip $(shell r2pm -H R2_LDFLAGS 2>/dev/null) $(shell r2pm -H R2_LIBS 2>/dev/null))
LDFLAGS += $(shell pkg-config --libs r_core 2>/dev/null || echo "$(if $(R2PM_LINK_FLAGS),$(R2PM_LINK_FLAGS),-lr_core -lr_util)")
LDFLAGS += $(shell pkg-config --libs r_core 2>/dev/null || echo "-L/usr/local/lib -lr_core -lr_config -lr_debug -lr_bin -lr_lang -lr_anal -lr_bp -lr_egg -lr_asm -lr_arch -lr_esil -lr_flag -lr_reg -lr_search -lr_syscall -lr_fs -lr_io -lr_socket -lr_cons -lr_magic -lr_muta -lr_util -ldl")
# Detect platform for shared library extension
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
@@ -36,7 +33,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 version.inc.c
HBC_HEADERS = $(shell find ../../include -name '*.h') utils.inc.c sbom.inc.c
# Object files
ARCH_OBJ = arch_hbc.o
@@ -209,7 +206,7 @@ help:
# Dependencies
$(HERMES_LIB):
@echo "Building libhbc library..."
$(MAKE) -C ../.. build/libhbc.a
$(MAKE) -C ../.. all
# Default target
.DEFAULT_GOAL := all
+44 -324
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <r_anal.h>
#include <hbc/hbc.h>
@@ -13,17 +13,9 @@
#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;
@@ -31,12 +23,6 @@ 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;
@@ -101,24 +87,6 @@ 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);
@@ -159,94 +127,7 @@ static bool opcode_is_conditional(u8 opcode) {
return false;
}
/* 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) {
static void parse_operands_and_set_ptr(RAnalOp *op, const ut8 *bytes, ut32 size, ut8 opcode, HermesArchSession *hs) {
HBCISA isa = hbc_isa_getv (hs->bytecode_version);
const Instruction *inst_set = isa.instructions;
if (!inst_set || opcode >= isa.count) {
@@ -254,211 +135,91 @@ static void parse_operands(RAnalOp *op, const ut8 *bytes, ut32 size, ut8 opcode,
}
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");
}
}
size_t pos = 1; /* Skip opcode byte */
ut32 values[6] = { 0 };
// Parse operands
ut32 operand_values[6] = { 0 };
size_t pos = 1; // Skip opcode byte
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 + 1 <= size) {
value = bytes[pos];
if (pos < size) {
operand_values[i] = bytes[pos];
pos += 1;
valid = true;
}
break;
case OPERAND_TYPE_UINT16:
if (pos + 2 <= size) {
value = r_read_le16 (bytes + pos);
if (pos + 1 < size) {
operand_values[i] = (bytes[pos + 1] << 8) | 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 + 4 <= size) {
value = r_read_le32 (bytes + pos);
if (pos + 3 < size) {
operand_values[i] = (bytes[pos + 3] << 24) | (bytes[pos + 2] << 16) |
(bytes[pos + 1] << 8) | bytes[pos];
pos += 4;
valid = true;
}
break;
case OPERAND_TYPE_DOUBLE:
if (pos + 8 <= size) {
ut64 bits = r_read_le64 (bytes + pos);
memcpy (&double_value, &bits, sizeof (double_value));
/* Double is 8 bytes - just advance position, value not used for ptr setting */
if (pos + 7 < size) {
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;
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;
// Check if this operand is a string ID
if (inst->operands[i].operand_meaning == OPERAND_MEANING_STRING_ID) {
ut32 string_id = operand_values[i];
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) {
ref_addr = HBC_VADDR_BASE + meta.offset;
op->ptr = (st64)ref_addr;
string_byte_length = meta.isUTF16? (ut64)meta.length * 2: meta.length;
op->ptr = (st64) (HBC_VADDR_BASE + meta.offset);
/* 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 = (int)string_byte_length;
op->ptrsize = meta.isUTF16? (int) (meta.length * 2): (int)meta.length;
}
}
} else if (operand->operand_meaning == OPERAND_MEANING_FUNCTION_ID) {
ut32 function_id = value;
}
// 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];
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) {
ref_addr = HBC_VADDR_BASE + fi.offset;
op->ptr = (st64)ref_addr;
// name = fi.name;
offset = fi.offset;
(void)fi.size;
(void)fi.param_count;
op->ptr = (st64) (HBC_VADDR_BASE + offset);
}
}
}
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;
@@ -511,19 +272,18 @@ static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
/* asm.syntax=camel → convert snake_case mnemonic to CamelCase */
if (camel && sinfo.text) {
char *camel_text = r_str_snake_to_camel (sinfo.text);
if (camel_text) {
free (sinfo.text);
sinfo.text = camel_text;
}
char camel_buf[128];
hbc_snake_to_camel (sinfo.text, camel_buf, sizeof (camel_buf));
free (sinfo.text);
sinfo.text = strdup (camel_buf);
}
op->mnemonic = sinfo.text? sinfo.text: strdup ("unk");
op->size = sinfo.size? sinfo.size: 1;
op->type = R_ANAL_OP_TYPE_UNK;
op->family = R_ANAL_OP_FAMILY_CPU;
/* Parse structured operands and set ptr for string/function references. */
parse_operands (op, op->bytes, op->size, sinfo.opcode, sinfo.canonical_opcode, hs, mask);
/* Parse operands and set ptr for string/function references */
parse_operands_and_set_ptr (op, op->bytes, op->size, sinfo.opcode, hs);
if (mask & R_ARCH_OP_MASK_ESIL && sinfo.text) {
set_esil (op, op->bytes, op->addr);
@@ -749,7 +509,6 @@ 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:
@@ -882,8 +641,6 @@ 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;
@@ -894,48 +651,6 @@ 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;
@@ -944,8 +659,14 @@ static char *regs(RArchSession *s) {
return NULL;
}
/* Hermes frame arguments and VM registers have no fixed ABI homes. */
/* Define special registers */
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");
@@ -975,7 +696,6 @@ const RArchPlugin r_arch_plugin_r2hermes = {
.regs = regs,
.info = archinfo,
.mnemonics = mnemonics,
.esilcb = esilcb,
.init = init,
.fini = fini,
};
+1 -1
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <r_asm.h>
+17 -239
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <r_bin.h>
#include <r_bin_dwarf.h>
@@ -11,10 +11,6 @@
typedef struct {
HBC *hbc;
#if R2_ABIVERSION >= 142
RVecRBinTrycatch trycatch;
bool trycatch_loaded;
#endif
} HBCBinObj;
static bool check(RBinFile *bf, RBuffer *b) {
@@ -32,13 +28,13 @@ static bool load(RBinFile *bf, RBuffer *buf, ut64 R_UNUSED loadaddr) {
HBC *hbc = NULL;
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;
if (bo) {
bo->hbc = hbc;
bf->bo->bin_obj = bo;
bf->buf = buf;
return true;
}
hbc_safe_close (&hbc);
}
}
return false;
@@ -47,9 +43,6 @@ 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);
}
@@ -194,6 +187,9 @@ static void add_pool_section(RList *list, const char *name, ut64 paddr, ut64 siz
return;
}
RBinSection *s = R_NEW0 (RBinSection);
if (!s) {
return;
}
s->name = strdup (name);
s->size = size;
s->vsize = size;
@@ -249,110 +245,10 @@ 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) {
@@ -369,7 +265,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;
set_symbol_size (sym, 1);
sym->size = 1;
sym->ordinal = b->string_id;
sym->type = R_BIN_TYPE_OBJECT_STR;
sym->bind = R_BIN_BIND_GLOBAL_STR;
@@ -423,16 +319,7 @@ static RList *symbols(RBinFile *bf) {
symbol->paddr = fi.offset;
symbol->vaddr = HBC_VADDR_BASE + fi.offset;
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->size = fi.size;
symbol->ordinal = i;
symbol->type = R_BIN_TYPE_FUNC_STR;
symbol->bits = 32;
@@ -461,7 +348,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;
set_symbol_size (sym, 1);
sym->size = 1;
sym->type = R_BIN_TYPE_OBJECT_STR;
sym->bits = 32;
r_list_append (symbols, sym);
@@ -564,6 +451,9 @@ static R_UNOWNED RList *lines(RBinFile *bf) {
bf->addrline.al_add (&bf->addrline, addr, file, NULL, sl->line, sl->column);
}
RBinAddrline *row = R_NEW0 (RBinAddrline);
if (!row) {
continue;
}
const RBinAddrline *stored = has_al? bf->addrline.al_get (&bf->addrline, addr): NULL;
if (stored) {
*row = *stored;
@@ -622,7 +512,6 @@ 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);
}
@@ -630,111 +519,6 @@ static RList *strings_list(RBinFile *bf) {
return ret;
}
#if R2_ABIVERSION >= 121
static const char *classify_document(const char *str, const char **document, const char **label) {
const char *doc = *document = r_str_trim_head_ro (str);
const bool xml = !r_str_ncasecmp (doc, "<?xml", 5);
const char *svg = xml? r_str_casestr (doc, "<svg"): doc;
if (svg && !r_str_ncasecmp (svg, "<svg", 4) && r_str_casestr (svg, "</svg>")) {
*label = "svg";
return "image/svg+xml";
}
if ((!r_str_ncasecmp (doc, "<!doctype html", 14) || !r_str_ncasecmp (doc, "<html", 5)) && r_str_casestr (doc, "</html>")) {
*label = "html";
return "text/html";
}
if (xml && strstr (doc, "?>")) {
*label = "xml";
return "application/xml";
}
if (!r_str_ncasecmp (doc, "-----BEGIN ", 11) && r_str_casestr (doc, "-----END ")) {
*label = "pem";
return "application/x-pem-file";
}
return NULL;
}
static bool add_hbc_resource(RBinFile *bf, u32 string_id, const char *label, u32 ordinal, const char *type, size_t type_len, const char *encoding, ut64 paddr, ut64 size) {
size_t index = RVecRBinResource_length (&bf->bo->resources_vec);
if (index > UT32_MAX) {
return false;
}
RBinResource *resource = RVecRBinResource_emplace_back (&bf->bo->resources_vec);
if (!resource) {
return false;
}
resource->name = r_str_newf ("string.%u.%s.%u", string_id, label, ordinal);
resource->type = r_str_ndup (type, type_len);
resource->encoding = encoding? strdup (encoding): NULL;
resource->origin = r_str_newf ("hbc.string.%u", string_id);
resource->vaddr = HBC_VADDR_BASE + paddr;
resource->paddr = paddr;
resource->size = size;
resource->id = UT64_MAX;
resource->index = (ut32)index;
resource->type_id = UT32_MAX;
resource->language_id = UT32_MAX;
resource->codepage = 65001;
resource->named = true;
return resource->name && resource->type && resource->origin && (!encoding || resource->encoding);
}
static bool load_resources(RBinFile *bf) {
HBC *hbc = get_hbc (bf);
if (!hbc) {
return false;
}
u32 string_count = hbc_string_count (hbc);
for (u32 i = 0; i < string_count; i++) {
const char *str = NULL;
HBCStringMeta meta;
if (hbc_get_string (hbc, i, &str).code != RESULT_SUCCESS || !str || hbc_get_string_meta (hbc, i, &meta).code != RESULT_SUCCESS || meta.isUTF16) {
continue;
}
if (!*str) {
continue;
}
const char *doc;
const char *doc_label;
const char *doc_type = classify_document (str, &doc, &doc_label);
ut64 doc_offset = doc - str;
if (doc_type && !add_hbc_resource (bf, i, doc_label, 0, doc_type, strlen (doc_type), NULL, meta.offset + doc_offset, meta.length - doc_offset)) {
return false;
}
const char *cursor = str;
u32 data_ordinal = 0;
const char *data;
while ((data = r_str_casestr (cursor, "data:"))) {
const char *comma = strchr (data + 5, ',');
if (!comma || comma - data > 256 || comma - (data + 5) < 7 || r_str_ncasecmp (comma - 7, ";base64", 7)) {
cursor = data + 5;
continue;
}
const char *payload = comma + 1;
const char *data_end = payload + strspn (payload, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=%");
if (data_end - payload < 4) {
cursor = data + 5;
continue;
}
const char *mime_end = memchr (data + 5, ';', comma - (data + 5));
size_t mime_len = mime_end - (data + 5);
const char *data_type = data + 5;
if (!mime_len) {
data_type = "text/plain";
mime_len = strlen (data_type);
}
if (!add_hbc_resource (bf, i, "data", data_ordinal++, data_type, mime_len, "data-uri", meta.offset + (ut64) (data - str), (ut64) (data_end - data))) {
return false;
}
cursor = data_end;
}
}
return true;
}
#endif
#if R2_ABIVERSION >= 104
static bool sections_vec(RBinFile *bf) {
if (!bf || !bf->bo) {
@@ -1103,14 +887,8 @@ 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
.load_resources = &load_resources,
#endif
};
#ifndef R2_PLUGIN_INCORE
+57 -170
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2 core plugin for Hermes bytecode decompilation */
@@ -85,7 +85,10 @@ static Result hbc_load_current_binary(HbcContext *ctx, RCore *core) {
}
/* Open HBC from buffer */
RETURN_IF_ERROR (hbc_open_from_buffer (bf->buf, &ctx->hbc));
Result res = hbc_open_from_buffer (bf->buf, &ctx->hbc);
if (res.code != RESULT_SUCCESS) {
return res;
}
ctx->core = core;
ctx->file_path = strdup (file_path);
@@ -397,7 +400,6 @@ static void cmd_list_eval_functions(HbcContext *ctx, RCore *core, const char *ar
for (u32 i = 0; i < sites.count; i++) {
const HBCEvalSite *site = &sites.sites[i];
ut64 address = HBC_VADDR_BASE + (ut64)site->address;
ut64 function_address = HBC_VADDR_BASE + (ut64)site->function_address;
if (quiet) {
r_cons_printf (core->cons, "0x%08" PFMT64x "\n", address);
} else {
@@ -406,7 +408,7 @@ static void cmd_list_eval_functions(HbcContext *ctx, RCore *core, const char *ar
address,
site->function_id,
site->offset,
function_address,
HBC_VADDR_BASE + (ut64)site->function_address,
site->strict? "true": "false",
r_str_get_fail (site->function_name, "unknown"));
}
@@ -741,8 +743,7 @@ static void register_r2_artifacts(RCore *core, const HBCLiteralEntry *e) {
if (!e->paddr) {
return;
}
ut64 base = r_config_get_b (core->config, "io.va")? HBC_VADDR_BASE: 0;
ut64 vaddr = base + e->paddr;
ut64 vaddr = HBC_VADDR_BASE + (ut64)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);
@@ -750,7 +751,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 = base + e->xref_addrs[i];
ut64 from = HBC_VADDR_BASE + (ut64)e->xref_addrs[i];
r_anal_xrefs_set (core->anal, from, vaddr, R_ANAL_REF_TYPE_DATA);
}
}
@@ -798,11 +799,17 @@ static Result lit_list_collect(HbcContext *ctx, RCore *core, const HBCLiteralEnt
*out_arr = NULL;
*out_n = 0;
HBC *hbc = NULL;
RETURN_IF_ERROR (ensure_hbc_loaded (ctx, core, &hbc));
Result r = ensure_hbc_loaded (ctx, core, &hbc);
if (r.code != RESULT_SUCCESS) {
return r;
}
if (!ensure_lit_cache_populated (core, hbc)) {
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "literal cache not populated");
}
RETURN_IF_ERROR (hbc_literals_list (hbc, out_arr, out_n));
r = hbc_literals_list (hbc, out_arr, out_n);
if (r.code != RESULT_SUCCESS) {
return r;
}
return SUCCESS_RESULT ();
}
@@ -813,10 +820,9 @@ 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 = base + e->paddr;
const ut64 addr = (ut64)HBC_VADDR_BASE + e->paddr;
const char *fmt = e->formatted? e->formatted: "";
r_cons_printf (core->cons, "0x%" PFMT64x " %s\n", addr, fmt);
}
@@ -847,16 +853,18 @@ 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;
bool found = false;
const HBCLiteralEntry *match = NULL;
for (u32 i = 0; i < n; i++) {
if (arr[i].paddr == target_paddr) {
print_xref_line (core, &arr[i], va);
found = true;
match = &arr[i];
break;
}
}
if (!found) {
if (!match) {
R_LOG_ERROR ("no literal at that address");
return;
}
print_xref_line (core, match, va);
return;
}
for (u32 i = 0; i < n; i++) {
@@ -904,23 +912,19 @@ 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",
"%-6s n=%-4u id=(%u,%u) %s=0x%08" PFMT64x " xrefs=%u %s\n",
kind_label (e->kind),
e->num_items,
e->primary_id,
e->secondary_id,
pavastr,
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: "");
(ut64)HBC_VADDR_BASE + e->paddr,
e->xref_count,
e->formatted? e->formatted: "");
}
}
@@ -961,16 +965,18 @@ 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 *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);
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);
}
}
/* 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", vaddr, from);
r_cons_printf (core->cons, "axd 0x%" PFMT64x " 0x%" PFMT64x "\n", from, vaddr);
}
}
@@ -992,16 +998,18 @@ 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;
bool found = false;
const HBCLiteralEntry *match = NULL;
for (u32 i = 0; i < n; i++) {
if (arr[i].paddr == target_paddr) {
print_r2_for_entry (core, &arr[i], va);
found = true;
match = &arr[i];
break;
}
}
if (!found) {
if (!match) {
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,33 +1066,29 @@ 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 and constructor addresses (auto-scans if empty)\n"
" r2hermes-L List cached literals (auto-scans code if cache is empty)\n"
" r2hermes-Lj List as JSON (auto-scans if needed)\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-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-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 all literals sharing that pool address\n"
" With addr: print oneliners for that single literal only\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";
" r2hermes-L? This help\n"
"\nDeprecated alias: pd:hL[subcmd]\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-V[j?] - Show bytecode and React Native versions (HBC or libhermes.so)\n";
"r2hermes-S[jr?] - emit SBOM from SLP literals (j=CycloneDX JSON, r=raw input)\n";
r_cons_print (core->cons, msg);
}
@@ -1102,6 +1106,7 @@ 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");
}
@@ -1124,11 +1129,9 @@ static void cmd_literals(HbcContext *ctx, RCore *core, const char *arg) {
cmd_lit_list_xrefs (ctx, core, arg + 1);
break;
case 'p':
{
HBCLiteralKind kind = (arg[1] == 'o')? HBC_LIT_OBJECT: HBC_LIT_ARRAY;
cmd_lit_scan_pool (ctx, core, kind);
break;
}
HBCLiteralKind kind = (arg[1] == 'o')? HBC_LIT_OBJECT: HBC_LIT_ARRAY;
cmd_lit_scan_pool (ctx, core, kind);
break;
case 'R':
cmd_lit_reset (ctx, core);
break;
@@ -1151,127 +1154,11 @@ 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;
@@ -1284,9 +1171,6 @@ 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);
@@ -1344,6 +1228,10 @@ 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;
@@ -1512,7 +1400,6 @@ R_API RLibStruct radare_plugin = {
.type = R_LIB_TYPE_CORE,
.data = (void *)&r_core_plugin_r2hermes,
.version = R2_VERSION,
.abiversion = R2_ABIVERSION,
.pkgname = "r2hermes"
.abiversion = R2_ABIVERSION
};
#endif
+1 -1
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* Core HBC plugin with plugin registration enabled for r2one */
+14 -14
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
static void set_esil(RAnalOp *op, const u8 *bytes, ut64 addr) {
if (!bytes || !op) {
@@ -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 ("==,$z")
case OP_StrictEq: E3 ("==")
case OP_Neq:
case OP_StrictNeq: E3 ("==,$z,!")
case OP_StrictNeq: E3 ("==,!")
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, "%u,HBC_LOAD_PARAM,r%u,=", bytes[2], bytes[1]);
r_strbuf_setf (&op->esil, "arg%u,r%u,=", bytes[2], bytes[1]);
break;
case OP_LoadParamLong:
r_strbuf_setf (&op->esil, "%u,HBC_LOAD_PARAM,r%u,=", r_read_le32 (bytes + 2), bytes[1]);
r_strbuf_setf (&op->esil, "arg%u,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,==,$z,?{,THROW,}", bytes[1]);
r_strbuf_setf (&op->esil, "r%u,UNDEFINED,==,?{,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 ("==,$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,!")
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 ("==,!")
/* aggregate / opaque ops with no precise ESIL model */
case OP_Unreachable:
+2 -2
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* CycloneDX SBOM generation for Hermes bytecode.
*
@@ -109,7 +109,7 @@ static bool sbom_is_version_spec(const char *version) {
"patch:",
"portal:",
"workspace:",
};
};
for (size_t i = 0; i < sizeof (prefixes) / sizeof (prefixes[0]); i++) {
if (r_str_startswith (version, prefixes[i])) {
return true;
+16 -2
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#ifndef R2HERMES_UTILS_INC_C
#define R2HERMES_UTILS_INC_C
@@ -9,7 +9,21 @@ static inline Result hbc_open_from_buffer(RBuffer *buf, HBC **out_hbc) {
if (!buf || !out_hbc) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
}
return hbc_open_from_rbuffer (buf, out_hbc);
ut64 size = r_buf_size (buf);
if (size == 0 || size > SIZE_MAX) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid buffer size");
}
ut8 *data = malloc (size);
if (!data) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate buffer");
}
if ((ut64)r_buf_read_at (buf, 0, data, size) != size) {
free (data);
return ERROR_RESULT (RESULT_ERROR_READ, "Failed to read buffer");
}
Result res = hbc_open_from_memory (data, size, out_hbc);
free (data);
return res;
}
static inline void hbc_safe_close(HBC **hbc) {
-145
View File
@@ -1,145 +0,0 @@
/* 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);
}
+14 -4
View File
@@ -1,4 +1,4 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/common.h>
#include <hbc/hbc.h>
@@ -57,7 +57,17 @@ static Result errorf(ResultCode code, const char *fmt, ...) {
va_start (ap, fmt);
vsnprintf (buf, sizeof (buf), fmt, ap);
va_end (ap);
return (Result){ code, buf };
Result r = { code, buf };
return r;
}
static void eprintf(const char *fmt, ...) {
va_list ap;
fputs ("Error: ", stderr);
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fputc ('\n', stderr);
}
static bool streq(const char *a, const char *b) {
@@ -1197,7 +1207,7 @@ int main(int argc, char **argv) {
const Command *cmd = find_command (argv[1]);
if (!cmd) {
print_usage (ctx.program_name);
fprintf (stderr, "Error: Unknown command: %s\n", argv[1]);
eprintf ("Unknown command: %s", argv[1]);
return 1;
}
int rest_argc = argc - 2;
@@ -1207,7 +1217,7 @@ int main(int argc, char **argv) {
r = cmd->handler (&ctx, rest_argc, rest_argv);
}
if (r.code != RESULT_SUCCESS) {
fprintf (stderr, "Error: %s\n", r.error_message[0]? r.error_message: "Unknown error");
eprintf ("%s", r.error_message[0]? r.error_message: "Unknown error");
return 1;
}
return 0;
-161
View File
@@ -1,161 +0,0 @@
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
+3 -1
View File
@@ -415,7 +415,9 @@ RUN
NAME=pd:hc short-circuit OR merges consecutive same-target conditional gotos
FILE=bins/hbc/index.android.bundle
CMDS=pd:hc 730~ \|\| r2===r20 \|\|
CMDS=<<EOF
pd:hc 730~ || r2===r20 ||
EOF
EXPECT=<<EOF
if (r1===r20 || r2===r20 || r3===r20 || r4===r20) goto loc_00055f28;
EOF
-115
View File
@@ -84,77 +84,6 @@ 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
@@ -253,50 +182,6 @@ 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,16 +81,3 @@ 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
@@ -1,59 +0,0 @@
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
-1
View File
@@ -8,7 +8,6 @@ if test_src.length() > 0
test_src,
link_with: libhbc,
include_directories: include_dirs,
dependencies: libhbc_link_deps,
install: false,
)
test('debug-info', run_tests, args: [meson.project_source_root()])
+4 -127
View File
@@ -1,7 +1,6 @@
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include "../src/lib/hbc_internal.h"
#include <hbc/opcodes.h>
#include <stdio.h>
#include <string.h>
@@ -19,9 +18,6 @@ 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);
@@ -78,81 +74,6 @@ 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);
@@ -160,10 +81,10 @@ static int test_function_bytecode_bounds(const char *root) {
HBC *hbc = NULL;
CHECK (hbc_open (path, &hbc).code == RESULT_SUCCESS);
CHECK (hbc->reader.function_headers);
CHECK (r_buf_size (hbc->reader.file_buffer) < UINT32_MAX);
CHECK (hbc->reader.file_buffer.size < UINT32_MAX);
FunctionHeader *fh = &hbc->reader.function_headers[0];
fh->offset = (u32)r_buf_size (hbc->reader.file_buffer) + 1;
fh->offset = (u32)hbc->reader.file_buffer.size + 1;
fh->bytecodeSizeInBytes = 1;
const u8 *ptr = (const u8 *)1;
@@ -209,53 +130,9 @@ static int test_decode_rejects_bad_overflow_string_index(void) {
return 0;
}
static int test_literal_addresses(void) {
u8 keys[] = { 0x71, 1, 0, 0, 0 };
u8 values[] = { 0x71, 20, 0, 0, 0, 0x71, 36, 0, 0, 0 };
ShapeTableEntry shape = { .key_buffer_offset = 0, .prop_count = 1 };
for (u32 version = 96; version <= 97; version++) {
HBC hbc = { 0 };
RVecHBCLiteralEntry_init (&hbc.lit_cache.entries);
hbc.reader.header.version = version;
hbc.reader.header.arrayBufferSize = sizeof (values);
hbc.reader.header.objKeyBufferSize = sizeof (keys);
hbc.reader.header.objValueBufferSize = sizeof (values);
hbc.reader.arrays = values;
hbc.reader.arrays_paddr = 0x100;
hbc.reader.object_keys = keys;
hbc.reader.object_keys_paddr = 0x200;
hbc.reader.object_values = values;
hbc.reader.object_values_paddr = version >= 97? 0x100: 0x300;
hbc.reader.object_shapes = &shape;
hbc.reader.object_shape_count = 1;
u32 num_items = version >= 97? 0: 1;
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 0, 0x400).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 5, 0x410).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 5, 0x420).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_ARRAY, 1, 5, 0, 0x430).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, sizeof (values), 0x440).code == RESULT_SUCCESS);
const HBCLiteralEntry *entries = NULL;
u32 count = 0;
CHECK (hbc_literals_list (&hbc, &entries, &count).code == RESULT_SUCCESS);
CHECK (count == 4);
CHECK (entries[0].paddr == hbc.reader.object_values_paddr);
CHECK (entries[1].paddr == hbc.reader.object_values_paddr + 5);
CHECK (entries[2].paddr == 0x105);
CHECK (entries[3].paddr == 0);
CHECK (!strcmp (entries[0].formatted, "{1: 20}"));
CHECK (!strcmp (entries[1].formatted, "{1: 36}"));
CHECK (!strcmp (entries[2].formatted, "[36]"));
CHECK (entries[1].xref_count == 2);
CHECK (entries[1].xref_addrs[0] == 0x410);
CHECK (entries[1].xref_addrs[1] == 0x420);
hbc_literals_reset (&hbc);
}
return 0;
}
int main(int argc, char **argv) {
const char *root = argc > 1? argv[1]: ".";
if (test_small_debug_info (root) || test_empty_debug_info (root) || test_exception_handlers (root) || test_opcode_operand_meanings () || test_function_bytecode_bounds (root) || test_decode_rejects_bad_overflow_string_index () || test_literal_addresses ()) {
if (test_small_debug_info (root) || test_empty_debug_info (root) || test_function_bytecode_bounds (root) || test_decode_rejects_bad_overflow_string_index ()) {
return 1;
}
return 0;