mirror of
https://github.com/radareorg/r2hermes.git
synced 2026-11-07 23:02:42 -10:00
Compare commits
1
Commits
main
..
rebrandcli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
991cc90760 |
No files matched your search
@@ -4,5 +4,8 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "##build "
|
||||
prefix-development: "##build "
|
||||
labels:
|
||||
- buildsystem
|
||||
+17
-68
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
.pmai/
|
||||
build/
|
||||
bin/
|
||||
third_party/
|
||||
@@ -9,4 +8,3 @@ third_party/
|
||||
test/bins
|
||||
*.swp
|
||||
include/hbc/version.h
|
||||
*.zip
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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,4 +1,4 @@
|
||||
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
|
||||
/* radare2 - BSD - Copyright 2025-2026 - pancake */
|
||||
|
||||
#ifndef LIBHBC_ENCODER_H
|
||||
#define LIBHBC_ENCODER_H
|
||||
|
||||
@@ -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,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
|
||||
|
||||
@@ -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
@@ -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,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
@@ -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)
|
||||
@@ -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,4 +1,4 @@
|
||||
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
|
||||
/* radare2 - BSD - Copyright 2025-2026 - pancake */
|
||||
|
||||
#ifdef R2_CORE_H
|
||||
#include "core/deps.h"
|
||||
|
||||
+729
-717
File diff suppressed because it is too large.
Load diff
@@ -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
@@ -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));
|
||||
|
||||
+259
-249
File diff suppressed because it is too large.
Load diff
+93
-238
@@ -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
@@ -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
@@ -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
@@ -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,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 +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 },
|
||||
};
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
@@ -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 },
|
||||
};
|
||||
@@ -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, "]"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+338
-290
File diff suppressed because it is too large.
Load diff
+391
-61
@@ -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 ();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -1,4 +1,4 @@
|
||||
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
|
||||
/* radare2 - BSD - Copyright 2025-2026 - pancake */
|
||||
|
||||
#include <r_asm.h>
|
||||
|
||||
|
||||
+17
-239
@@ -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
@@ -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,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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user