Compare commits

...
Author SHA1 Message Date
dependabot[bot] 0e6189ae77 Bump actions/setup-python from 7.0.0 to 7.1.0
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/5fda3b95a4ea91299a34e894583c3862153e4b97...6d4fd22b20465ccc0f7354476588e8df7c9ca0e0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-10-29 00:43:32 +00:00
pancake 0b53e148fb Request trycatch script output in tests 2026-10-20 08:31:28 +02:00
pancake a93ca6767d Release 1.6.0 2026-10-19 17:29:47 +02:00
pancake e3f5b7e560 Use r2 6.2.4 in the ci 2026-10-19 17:29:14 +02:00
pancake 47279d355c Pin the actions 2026-10-19 17:28:11 +02:00
Abhi 3b08970ce8 Fix bindist triplet naming on runners with older r2pm
The 1.5.2 release shipped r2hermes-6.2.2---.zip for unix targets:
the bindist job pins radare2 6.2.2 whose r2pm lacks R2PM_OS/ARCH/BITS.
Fall back to uname (darwin/linux, x86/arm) so all targets get proper
triplet names.
2026-10-18 11:18:58 +05:30
pancake 0566ca00a8 Last minute fix for a missing string flag 2026-10-08 09:57:37 +02:00
pancake 4bfde1f33f Add -A to sync with r2* usage 2026-10-08 09:54:39 +02:00
pancake 6a243fbc1e Fix slp address location, honor io.va, baddr and construction instruction 2026-10-08 09:46:53 +02:00
pancake e073f29cb3 Register as r2hermes pkg 2026-10-06 16:55:28 +02:00
pancake e7bbd96fc7 Release 1.5.2 2026-10-06 00:44:05 +02:00
pancake 1370dc15ce Improve the ci for binary r2pm builds 2026-09-30 11:00:15 +02:00
pancake d448e54eb8 Fix integer overflow bug in the decompiler 2026-09-30 10:58:54 +02:00
pancake 5faf591cb0 Add the ability to extract react native version string from libhermes.so 2026-09-28 18:22:41 +02:00
pancake a9e5b02a47 Release 1.5.0 2026-09-07 14:01:34 +02:00
pancake ce854d747f Fix build with latest r2 6.2.2 2026-09-07 13:57:11 +02:00
pancake dfc3a0f788 Use R2V 6.2.2 in the ci 2026-09-07 13:48:58 +02:00
pancake 0e2bdf4171 Add Hermes SwitchImm targets to analysis ##arch 2026-08-26 19:57:17 +02:00
pancake fbeb335107 Expose async, generator and operand metadata via rbin 2026-08-26 19:31:01 +02:00
pancake 1021280afe Fix UTF-16 strings in parser and rbin metadata 2026-08-26 19:06:17 +02:00
pancake 9089923bf8 Expose trycatchblocks via rbin iw interface 2026-08-26 18:28:48 +02:00
pancake 6ef311dbf7 Fix esil for comparison operations 2026-08-26 17:14:52 +02:00
pancake d89306435e Expose string byte lengths in opex 2026-08-26 16:07:06 +02:00
pancake 9eebb355ec Fix LoadParam ESIL frame slot modeling 2026-08-26 15:50:51 +02:00
pancake e80509d031 Fix test 2026-08-26 12:02:28 +02:00
pancake 16d01a2f15 Expose implicit Hermes call-frame arguments via opex 2026-08-26 11:59:48 +02:00
pancake 09752c456f Fix fake ABI roles in the HBC register profile 2026-08-26 11:42:50 +02:00
pancake 5d647b945f Add HBC function argument and return metadata 2026-08-26 11:26:17 +02:00
pancake 514ed8e7f8 Expose resolved strings and function operands via opex 2026-08-26 09:49:35 +02:00
pancake 4863d19818 Fix build for latest r2 (>=6.2.1) 2026-08-26 09:41:14 +02:00
pancake bcbe90ae0b Release 1.4.8 2026-08-07 12:56:15 +02:00
pancake 4083c14a12 Fix copyright header on all 65 source files 2026-07-28 14:32:57 +01:00
Ahmethan G. 3ff4630ed5 Add the missing v98 instruction table 2026-07-28 14:28:40 +01:00
pancake 5c3e46fa45 Add load_resources for r2abi>=121 (aka r2-620) 2026-07-16 18:52:51 +02:00
pancake b1d6bdcb9a Release 1.4.6 2026-07-16 12:57:06 +02:00
pancake 49ef0b30d3 Fix r2plugin ci 2026-07-09 11:17:02 +02:00
pancake 62ad83fac0 Use R_NEW0 2026-07-09 09:03:28 +02:00
pancake b4fc0c6d45 Use RBuf from radare2 instead of BufferRead 2026-07-09 08:59:44 +02:00
pancake 1a9b277c5b Use RStrBuf instead of custom StringBuffer 2026-07-08 22:43:40 +02:00
pancake 399424823f Use RVec from radare2 instead of custom growing arrays
* libhbc now depends on r_util, which required changes in r2plugin/deps.mk
2026-07-08 22:22:10 +02:00
pancake 36582c4cc2 Fix build 2026-07-06 20:35:34 +02:00
pancake 797b8abcf1 Release 1.4.4 2026-07-06 19:34:33 +02:00
pancake 4baf92d5f3 Rename the cli tool to r2hermes instead of libhbctool 2026-07-03 21:02:03 +02:00
90 changed files with 4284 additions and 3360 deletions

No files matched your search

-3
View File
@@ -4,8 +4,5 @@ updates:
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "##build "
prefix-development: "##build "
labels:
- buildsystem
+68 -17
View File
@@ -1,5 +1,8 @@
name: CI
env:
R2V: 6.2.4
permissions:
contents: write
@@ -16,16 +19,18 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Install build deps
run: sudo apt-get update -y && sudo apt-get install -y build-essential autoconf automake libtool pkg-config
- name: Build library
run: make
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build library
run: make
- name: Build r2 plugin
run: make r2
- name: Run testsuite
@@ -52,7 +57,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
@@ -61,13 +66,15 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build Debian package
run: make -C dist/debian SUDO=
- name: Upload Debian package artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: debian-package
path: dist/debian/*.deb
@@ -79,10 +86,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: 3.12.x
@@ -93,7 +100,9 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build and install with Meson
shell: cmd
@@ -122,22 +131,56 @@ jobs:
run: |
Compress-Archive -Path install\* -DestinationPath r2hermes-windows-x64.zip -Force
- name: Package r2pm binary
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path bindist\plugins,bindist\bin | Out-Null
Copy-Item install\lib\radare2\plugins\*.dll bindist\plugins
Copy-Item install\bin\r2hermes.exe bindist\bin
Compress-Archive -Path bindist\* -DestinationPath "r2hermes-$env:R2V-windows-x86-64.zip" -Force
- name: Upload Windows package artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: windows-package
path: r2hermes-windows-x64.zip
path: r2hermes-*.zip
if-no-files-found: error
bindist:
name: Build r2pm binary (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install radare2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Package r2pm binary
run: make bindist
- name: Upload r2pm binary artifact
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: bindist-${{ matrix.os }}
path: r2hermes-*.zip
if-no-files-found: error
release:
name: Create release and upload artifacts
runs-on: ubuntu-latest
needs: [build, debian, windows]
needs: [build, debian, windows, bindist]
if: needs.build.outputs.tag != ''
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
@@ -149,25 +192,33 @@ jobs:
ls -l libhbc*
- name: Download Debian package artifact
uses: actions/download-artifact@v8
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: debian-package
path: release-artifacts
- name: Download Windows package artifact
uses: actions/download-artifact@v8
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: windows-package
path: release-artifacts/windows
- name: Download r2pm binary artifacts
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: bindist-*
merge-multiple: true
path: release-artifacts/bindist
- name: Create GitHub Release
uses: softprops/[email protected].1
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
tag_name: ${{ needs.build.outputs.tag }}
files: |
./libhbc-${{ needs.build.outputs.tag }}.tgz
./release-artifacts/*.deb
./release-artifacts/windows/*.zip
./release-artifacts/bindist/*.zip
make_latest: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+7 -2
View File
@@ -1,5 +1,8 @@
name: Meson Build
env:
R2V: 6.2.2
on:
push:
workflow_dispatch: {}
@@ -11,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
@@ -20,7 +23,9 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build library and r2 plugin with meson
run: meson setup builddir && meson compile -C builddir
+3 -6
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Checkout TinyCC repository
@@ -30,12 +30,11 @@ jobs:
run: |
git clone --depth=1 https://github.com/radareorg/radare2.git
cd radare2
echo core.r2hermes > libr/xps/static.cfg
export CC=tcc
echo core.r2hermes > libr/xps/static.cfg
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
@@ -50,7 +49,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Install build dependencies
@@ -64,8 +63,6 @@ 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
+9 -4
View File
@@ -1,5 +1,8 @@
name: Windows Build
env:
R2V: 6.2.2
on:
push:
workflow_dispatch: {}
@@ -11,10 +14,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
with:
python-version: 3.12.x
@@ -25,7 +28,9 @@ jobs:
- name: Install radare2
id: r2
uses: radareorg/github-actions@v2
uses: radareorg/github-actions@4ae27a8b29a049ac0fd5119f6eda37ecfc64ead8 # v3
with:
version: ${{ env.R2V }}
- name: Build and install with Meson
shell: cmd
@@ -55,7 +60,7 @@ jobs:
Compress-Archive -Path install\* -DestinationPath r2hermes-windows-x64.zip -Force
- name: Upload artifacts
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: windows-package
path: r2hermes-windows-x64.zip
+1
View File
@@ -8,3 +8,4 @@ third_party/
test/bins
*.swp
include/hbc/version.h
*.zip
+4 -4
View File
@@ -7,19 +7,19 @@ Use this guide to navigate the code, extend features, and avoid common pitfalls
**Repo Layout**
- `src/lib` C sources for the library
- `src/r2/` radare2 plugin sources (optional)
- `src/tool/` source code of the libhbctool program
- `src/tool/` source code of the r2hermes program
- `include/` public and internal headers
- `bin/` where the libhbctool CLI program is compiled
- `bin/` where the r2hermes CLI program is compiled
- `build/` static library and objects
- `tests/` testsuite based on `r2r` tool
**Build & Run**
- Build library + libhbctool cli tool: `make` (no debug messages)
- Build library + r2hermes cli tool: `make` (no debug messages)
- Build with address sanitizer checks: `make asan`
- Build library + radare2 plugins + user-install: `make r2`
- Format the source with `make fmt` (requires `clang-format-radare2`)
- Clean: `make clean`
- Run CLI: `./bin/libhbctool <command> <input> [output]`
- Run CLI: `./bin/r2hermes <command> <input> [output]`
**Public API**
- Primary header: `include/hbc/hbc.h`
+52 -8
View File
@@ -1,5 +1,9 @@
CC?= gcc
CFLAGS?=-Wall -Wextra -std=c11 -pedantic -O2 -fPIC -fvisibility=hidden -D_POSIX_C_SOURCE=200809L
ifeq ($(shell uname),Darwin)
# macOS hides memmem() (used by r_util headers) under strict _POSIX_C_SOURCE
CFLAGS+=-D_DARWIN_C_SOURCE
endif
DEBUG_FLAGS?=-g -DDEBUG -DHBC_DEBUG_LOGGING=1
VERSION=$(shell grep "^[[:space:]]*version:" meson.build | cut -d "'" -f 2)
@@ -12,6 +16,7 @@ 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)
@@ -19,17 +24,18 @@ DECOMPILE_SRC = $(wildcard $(SRC_DIR)/decompilation/*.c)
OPCODES_SRC = $(filter-out $(SRC_DIR)/opcodes/%.inc.c,$(wildcard $(SRC_DIR)/opcodes/*.c))
LIB_SRC = $(UTILS_SRC) $(PARSERS_SRC) $(DISASM_SRC) $(DECOMPILE_SRC) $(OPCODES_SRC) \
$(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/libhbctool.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))
LIB_RELOC_OBJ = $(BUILD_DIR)/libhbc_all.o
MAIN_OBJ = $(BUILD_DIR)/libhbctool.o
MAIN_RELOC_OBJ = $(BUILD_DIR)/libhbctool_all.o
MAIN_OBJ = $(BUILD_DIR)/r2hermes.o
MAIN_RELOC_OBJ = $(BUILD_DIR)/r2hermes_all.o
## Artifacts
BIN_FILE = $(BIN_DIR)/libhbctool
BIN_FILE = $(BIN_DIR)/r2hermes
STATIC_LIB = $(BUILD_DIR)/libhbc.a
# Include paths
@@ -64,7 +70,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)
$(CC) $(CFLAGS) -o $@ $(MAIN_RELOC_OBJ) $(R2_LDFLAGS) -lr_util
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(HBC_HEADERS) | $(VH)
@mkdir -p $(dir $@)
@@ -74,7 +80,7 @@ $(MAIN_OBJ): $(MAIN_SRC) $(HBC_HEADERS) | $(VH)
$(CC) $(CFLAGS) -fvisibility=default $(INCLUDES) -c $< -o $@
clean:
rm -rf $(BUILD_DIR) $(BIN_DIR)/libhbctool
rm -rf $(BUILD_DIR) $(BIN_DIR)/r2hermes
rm -f $(VH)
$(MAKE) -C src/r2 clean
@@ -84,7 +90,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
$(CC) $(CFLAGS) -fvisibility=default $(INCLUDES) -o $@ $(TEST_SRC) -L$(BUILD_DIR) -lhbc $(R2_LDFLAGS) -lr_util
test: $(BIN_FILE) $(TEST_BIN)
$(TEST_BIN)
@@ -97,6 +103,7 @@ test: $(BIN_FILE) $(TEST_BIN)
$(BIN_FILE) Ej test/bins/hbc/bespoke_eval.hbc | grep -q '"strict":false'
$(BIN_FILE) h test/bins/hbc/bespoke_eval.hbc | grep -q 'Source Locations: 13 bytes'
$(BIN_FILE) h test/bins/hbc/index.android.bundle | grep -q 'Source Locations: 0 bytes'
$(BIN_FILE) s test/bins/hbc/index.android.bundle 2959 | grep -q 'name=•'
$(BIN_FILE) libs test/bins/hbc/index.android.bundle | grep -q 'react-native'
r2:
@@ -111,6 +118,43 @@ user-install user-uninstall:
$(MAKE) -C src/r2
$(MAKE) -C src/r2 $@
# Binary package for r2pm -bi: plugins/ and bin/ for this r2 version and platform
# Binary package for r2pm -bi: plugins/ and bin/ for this r2 version and platform
# R2PM_OS/ARCH/BITS require a recent r2pm; fall back to uname when missing
R2PM_OS_SH=$(shell r2pm -H R2PM_OS 2>/dev/null)
R2PM_ARCH_SH=$(shell r2pm -H R2PM_ARCH 2>/dev/null)
R2PM_BITS_SH=$(shell r2pm -H R2PM_BITS 2>/dev/null)
ifeq ($(R2PM_OS_SH),)
ifeq ($(shell uname -s),Darwin)
R2PM_OS_SH=darwin
else
R2PM_OS_SH=linux
endif
endif
ifeq ($(R2PM_ARCH_SH),)
ifeq ($(shell uname -m),x86_64)
R2PM_ARCH_SH=x86
else ifeq ($(shell uname -m),aarch64)
R2PM_ARCH_SH=arm
else ifeq ($(shell uname -m),arm64)
R2PM_ARCH_SH=arm
else
R2PM_ARCH_SH=$(shell uname -m)
endif
endif
ifeq ($(R2PM_BITS_SH),)
R2PM_BITS_SH=64
endif
BINDIST_ZIP=r2hermes-$(shell r2 -qv)-$(R2PM_OS_SH)-$(R2PM_ARCH_SH)-$(R2PM_BITS_SH).zip
bindist: all
$(MAKE) -C src/r2
rm -rf $(BUILD_DIR)/bindist $(BINDIST_ZIP)
$(MAKE) -C src/r2 user-install R2_PLUGDIR=$(CURDIR)/$(BUILD_DIR)/bindist/plugins
mkdir -p $(BUILD_DIR)/bindist/bin
cp -f $(BIN_FILE) $(BUILD_DIR)/bindist/bin/
cd $(BUILD_DIR)/bindist && zip -r $(CURDIR)/$(BINDIST_ZIP) plugins bin
$(VH):
@mkdir -p $(dir $@)
echo '#ifndef LIBHBC_VERSION' > $@
@@ -120,4 +164,4 @@ $(VH):
echo '#define LIBHBC_VERSION_PATCH "$(shell echo $(VERSION) | cut -d . -f 3)"' >> $@
echo '#endif' >> $@
.PHONY: r2 test test2 all clean debug asan
.PHONY: r2 test test2 all clean debug asan bindist
+19 -1
View File
@@ -29,7 +29,7 @@ r2pm -ci r2hermes
### From source
Build the `libhbctool` cli tool and install the `radare2` plugin:
Build the `r2hermes` cli tool and install the `radare2` plugin:
```bash
make && make user-install
@@ -81,12 +81,30 @@ Usage: pd:h[subcommand]
[0x00000000]> r2hermes-?
Usage: r2hermes[-arg] # see also pd:h for decompilation
r2hermes-h - help message (same as r2hermes-?, see pd:h? too)
r2hermes-A - Import symbols and entrypoints
r2hermes-AA - Also import strings and SLP literal comments/xrefs
r2hermes-AAA - Also analyze bytecode references
r2hermes-AAAA - Also perform full analysis (aaa)
r2hermes-E[jq] - List direct eval instruction sites (j=JSON, q=addresses only)
r2hermes-H - Show file information and hash status
r2hermes-L[?] - SLP literal cache: list/scan/reset/format/toggle
r2hermes-S[jr?] - emit SBOM from SLP literals (j=CycloneDX JSON, r=raw input)
```
The analysis levels are cumulative, like `r2flutter-A` and `r2unity-A`.
Use `r2hermes-A` to import symbols and entrypoint flags, and `r2hermes-AA` to
also import string flags and metadata, plus SLP literal flags, comments, and
constructor references. `r2hermes-AAA` adds reference analysis over each
function's bytecode range; `r2hermes-AAAA` additionally runs `aaa` to analyze
functions and control flow. Addresses follow `io.va`, and the commands preserve
the current seek and flagspace. Use `r2hermes-A?` for analysis help.
`r2hermes-L` lists the constructor instruction addresses after `xrefs=`.
Object literal addresses point to their serialized values; their keys or shapes
can be shared by objects with different values. Pools can also share values, so
`r2hermes-Lx <addr>` lists every matching entry. Use the constructor addresses
on each row to navigate to that specific object. Text output follows `io.va`.
### Examples
```bash
+1 -1
View File
@@ -1,2 +1,2 @@
Hermes bytecode tools and radare2 plugins
r2hermes provides libhbctool, libhbc, and radare2 plugins to inspect, disassemble, assemble, and decompile React Native Hermes bytecode files.
r2hermes provides libhbc, and radare2 plugins to inspect, disassemble, assemble, and decompile React Native Hermes bytecode files.
+1 -1
View File
@@ -24,7 +24,7 @@ else
cd ../.. && $(MAKE) clean && $(MAKE) && $(MAKE) -C src/r2
rm -rf root
mkdir -p root/usr/bin
cp -f ../../bin/libhbctool root/usr/bin/
cp -f ../../bin/r2hermes root/usr/bin/
mkdir -p root/usr/lib
cp -f ../../build/libhbc.a root/usr/lib/
mkdir -p root/usr/include
+6 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_BYTECODE_H
#define LIBHBC_BYTECODE_H
@@ -7,6 +7,9 @@
#include <hbc/opcodes.h>
#include <hbc/parser.h>
/* Registers reserved by Hermes above an outgoing call's this/argument window. */
#define HBC_CALL_FRAME_RESERVED 6
/* Parsed instruction */
typedef struct {
const Instruction *inst;
@@ -43,7 +46,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, StringBuffer *out_string);
Result _hbc_instruction_to_string(ParsedInstruction *instruction, RStrBuf *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);
@@ -60,7 +63,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, BufferReader *bytecode, ParsedInstruction *out_instruction);
typedef Result(*OpcodeHandler)(HBCReader *reader, RBuffer *bytecode, ParsedInstruction *out_instruction);
/* Helpers to classify opcodes */
bool _hbc_is_jump_instruction(u8 opcode);
+26 -58
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_COMMON_H
#define LIBHBC_COMMON_H
@@ -11,23 +11,19 @@
#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
@@ -55,13 +51,6 @@ 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,
@@ -83,52 +72,31 @@ 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;
/* 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);
/* 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);
/* 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 = (expr); \
if (result.code != RESULT_SUCCESS) { \
return result; \
} \
} while (0)
#define SUCCESS_RESULT() ((Result){ RESULT_SUCCESS, "" })
#define ERROR_RESULT(code, message) ((Result){ code, message })
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)
/* Utility macros for error handling */
#define RETURN_IF_ERROR(expr) \
do { \
Result result = HBC_TO_RESULT (expr); \
if (result.code != RESULT_SUCCESS) { \
return result; \
} \
} while (0)
#endif /* LIBHBC_COMMON_H */
+53 -58
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_DECOMPILER_H
#define LIBHBC_DECOMPILER_H
@@ -8,17 +8,46 @@
#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 */
typedef struct BasicBlock {
struct BasicBlock {
u32 start_address;
u32 end_address;
bool may_be_cycling_anchor;
@@ -30,24 +59,15 @@ typedef struct BasicBlock {
bool is_conditional_jump_anchor;
bool is_switch_action_anchor;
ParsedInstruction *anchor_instruction;
u32 *jump_targets_for_anchor;
u32 jump_targets_count;
RVecHBCU32 jump_targets;
bool stay_visible;
/* Control flow graph */
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;
RVecBasicBlockPtr child_nodes;
RVecBasicBlockPtr parent_nodes;
RVecBasicBlockPtr error_handling_child_nodes;
RVecBasicBlockPtr error_handling_parent_nodes;
};
/* Environment for closure variable handling */
struct Environment {
@@ -70,11 +90,13 @@ typedef struct {
} AddressLabels;
/* Decompiled code nesting frame (e.g. for..in loop body scope) */
typedef struct {
typedef struct NestedFrame {
u32 start_address;
u32 end_address;
} NestedFrame;
R_VEC_TYPE(RVecNestedFrame, NestedFrame)
/* Function body decompilation state */
struct DecompiledFunctionBody {
bool is_global;
@@ -99,9 +121,7 @@ struct DecompiledFunctionBody {
ParsedInstruction **throw_anchors; /* Map from address to instruction */
u32 throw_anchors_count;
u32 *jump_targets; /* Set of addresses */
u32 jump_targets_count;
u32 jump_targets_capacity;
RVecHBCU32 jump_targets;
/* Function flags */
bool is_closure;
@@ -114,56 +134,31 @@ struct DecompiledFunctionBody {
Environment **local_items; /* Map from env_register to Environment */
u32 local_items_count;
u32 local_items_capacity;
Environment **owned_environments; /* Environments allocated for this function */
u32 owned_environments_count;
u32 owned_environments_capacity;
RVecEnvironmentPtr owned_environments;
/* Control flow structures */
BasicBlock **basic_blocks;
u32 basic_blocks_count;
u32 basic_blocks_capacity;
RVecBasicBlockPtr basic_blocks;
/* Decompiled-code nesting frames */
NestedFrame *nested_frames;
u32 nested_frames_count;
u32 nested_frames_capacity;
RVecNestedFrame nested_frames;
/* 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). */
u32 *forin_continue_targets;
u32 forin_continue_targets_count;
u32 forin_continue_targets_capacity;
RVecHBCU32 forin_continue_targets;
/* 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). */
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;
RVecDowhileLoop dowhile_loops;
/* 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. */
struct { u32 top; u32 exit; } *forever_loops;
u32 forever_loops_count;
u32 forever_loops_capacity;
RVecForeverLoop forever_loops;
/* Generated code */
TokenString *statements;
u32 statements_count;
u32 statements_capacity;
RVecHbcTokenString statements;
/* Parsed instructions */
ParsedInstructionList instructions;
@@ -186,7 +181,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;
StringBuffer output;
RStrBuf output;
} HermesDecompiler;
/* Function declarations */
@@ -196,10 +191,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, 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_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_function(HermesDecompiler *state, u32 function_id, Environment *parent_environment, int environment_id, bool is_closure, bool is_generator, bool is_async);
/* Transformation passes */
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, StringBuffer *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, RStrBuf *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, StringBuffer *out, LiteralsPrettyPolicy policy, bool suppress_comments);
Result _hbc_format_array_literal(HBCReader *r, u32 value_count, u32 array_id, RStrBuf *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, StringBuffer *out);
Result _hbc_format_property_from_string_id(HBCReader *r, u32 string_id, RStrBuf *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, StringBuffer *out);
Result _hbc_format_variadic_call(const ParsedInstruction *insn, RStrBuf *out);
#endif /* LIBHBC_LITERALS_H */
+119 -119
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_TOKEN_H
#define LIBHBC_TOKEN_H
@@ -10,97 +10,97 @@
struct HermesDecompiler;
struct Environment;
/* Token types */
/* HbcToken types */
typedef enum {
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;
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;
/* Base token structure */
typedef struct Token {
TokenType type;
struct Token *next;
} Token;
typedef struct HbcToken {
HbcTokenType type;
struct HbcToken *next;
} HbcToken;
/* Raw token with string content */
typedef struct {
Token base;
HbcToken base;
char *text;
} RawToken;
} HbcRawToken;
/* Register token (left-hand side) */
typedef struct {
Token base;
HbcToken base;
int reg_num;
} LeftHandRegToken;
} HbcLeftHandRegToken;
/* Register token (right-hand side) */
typedef struct {
Token base;
HbcToken base;
int reg_num;
} RightHandRegToken;
} HbcRightHandRegToken;
/* Assignment token (=) */
typedef struct {
Token base;
} AssignmentToken;
HbcToken base;
} HbcAssignmentToken;
/* Parenthesis tokens */
typedef struct {
Token base;
} LeftParenthesisToken;
HbcToken base;
} HbcLeftParenthesisToken;
typedef struct {
Token base;
} RightParenthesisToken;
HbcToken base;
} HbcRightParenthesisToken;
/* Dot accessor token (.) */
typedef struct {
Token base;
} DotAccessorToken;
HbcToken base;
} HbcDotAccessorToken;
/* Bind token (.bind ()) */
typedef struct {
Token base;
HbcToken base;
int reg_num;
} BindToken;
} HbcBindToken;
/* Return directive token */
typedef struct {
Token base;
} ReturnDirectiveToken;
HbcToken base;
} HbcReturnDirectiveToken;
/* Throw directive token */
typedef struct {
Token base;
} ThrowDirectiveToken;
HbcToken base;
} HbcThrowDirectiveToken;
/* Function table index token */
typedef struct {
Token base;
HbcToken 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;
} FunctionTableIndexToken;
} HbcFunctionTableIndexToken;
/* Jump condition tokens */
typedef struct {
Token base;
HbcToken base;
u32 target_address;
} JumpConditionToken;
} HbcJumpConditionToken;
typedef struct {
Token base;
HbcToken base;
u32 target_address;
} JumpNotConditionToken;
} HbcJumpNotConditionToken;
/* Environment tokens */
typedef struct {
Token base;
HbcToken base;
int reg_num;
int nesting_level;
} GetEnvironmentToken;
} HbcGetEnvironmentToken;
typedef struct {
Token base;
HbcToken base;
int reg_num;
int slot_index;
} LoadFromEnvironmentToken;
} HbcLoadFromEnvironmentToken;
typedef struct {
Token base;
HbcToken base;
int reg_num;
} NewEnvironmentToken;
} HbcNewEnvironmentToken;
typedef struct {
Token base;
HbcToken base;
int dest_register;
int parent_register;
int number_of_slots;
} NewInnerEnvironmentToken;
} HbcNewInnerEnvironmentToken;
/* Switch statement token */
typedef struct {
Token base;
HbcToken base;
int value_reg;
u32 jump_table_address;
u32 default_jump_address;
u32 unsigned_min_value;
u32 unsigned_max_value;
} SwitchImmToken;
} HbcSwitchImmToken;
/* Environment store token */
typedef struct {
Token base;
HbcToken base;
int env_register;
int slot_index;
int value_register;
} StoreToEnvironmentToken;
} HbcStoreToEnvironmentToken;
/* For-in loop tokens */
typedef struct {
Token base;
HbcToken base;
int obj_props_register;
int obj_register;
int iter_index_register;
int iter_size_register;
} ForInLoopInitToken;
} HbcForInLoopInitToken;
typedef struct {
Token base;
HbcToken base;
int next_value_register;
int obj_props_register;
int obj_register;
int iter_index_register;
int iter_size_register;
} ForInLoopNextIterToken;
} HbcForInLoopNextIterToken;
/* Generator tokens */
typedef struct {
Token base;
HbcToken base;
int result_out_reg;
int return_bool_out_reg;
} ResumeGeneratorToken;
} HbcResumeGeneratorToken;
typedef struct {
Token base;
HbcToken base;
u32 address;
} SaveGeneratorToken;
} HbcSaveGeneratorToken;
typedef struct {
Token base;
} StartGeneratorToken;
HbcToken base;
} HbcStartGeneratorToken;
/* Try-catch token */
typedef struct {
Token base;
HbcToken base;
int arg_register;
} CatchBlockStartToken;
} HbcCatchBlockStartToken;
/* Token string (list of tokens) */
/* HbcToken string (list of tokens) */
typedef struct {
Token *head;
Token *tail;
HbcToken *head;
HbcToken *tail;
ParsedInstruction *assembly;
} TokenString;
} HbcTokenString;
/* Function declarations */
Result _hbc_token_string_init(TokenString *token_string, ParsedInstruction *instruction);
void _hbc_token_string_cleanup(TokenString *token_string);
Result _hbc_token_string_init(HbcTokenString *token_string, ParsedInstruction *instruction);
void _hbc_token_string_cleanup(HbcTokenString *token_string);
/* 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);
/* 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);
/* Advanced token creation */
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 *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 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);
/* 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);
#endif /* LIBHBC_TOKEN_H */
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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 TokenString */
Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn, TokenString *out);
/* Translate a single parsed instruction into a HbcTokenString */
Result _hbc_translate_instruction_to_tokens(const ParsedInstruction *insn, HbcTokenString *out);
#endif /* LIBHBC_TRANSLATOR_H */
+2 -2
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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 */
StringBuffer output; /* The output buffer */
RStrBuf output; /* The output buffer */
HBCDisOptions options; /* Disassembly options */
u32 current_function_id; /* Function currently being disassembled */
} Disassembler;
+35 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_API_H
#define LIBHBC_API_H
@@ -57,14 +57,33 @@ typedef struct HBCStringMeta {
HBCStringKind kind;
} HBCStringMeta;
typedef enum {
HBC_FUNCTION_NONE = 0,
HBC_FUNCTION_ASYNC = 1u << 0,
HBC_FUNCTION_GENERATOR = 1u << 1
} HBCFunctionFlags;
/* Function metadata structure */
typedef struct {
const char *name; // Valid while provider is alive; caller must not free
u32 offset; // Bytecode offset in file
u32 size; // Size in bytes
u32 param_count; // Number of parameters
u32 flags; // HBCFunctionFlags
} HBCFunc;
/* Function-relative exception-handler range. */
typedef struct {
u32 start;
u32 end;
u32 target;
} HBCExceptionHandler;
typedef struct {
HBCExceptionHandler *handlers;
u32 count;
} HBCExceptionHandlerArray;
typedef struct {
u32 function_id;
u32 address; // File-absolute bytecode address
@@ -150,8 +169,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;
@@ -222,6 +241,12 @@ 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.
@@ -248,6 +273,13 @@ HBC_API Result hbc_get_header(HBC *hbc, HBCHeader *out);
*/
HBC_API Result hbc_get_function_info(HBC *hbc, u32 function_id, HBCFunc *out);
/** Get a copy of the exception handlers for one function. */
HBC_API Result hbc_get_function_exception_handlers(HBC *hbc, u32 function_id, HBCExceptionHandlerArray *out);
HBC_API void hbc_free_exception_handlers(HBCExceptionHandlerArray *arr);
/** Get the register-frame size for a function without exposing parser internals. */
HBC_API Result hbc_get_function_frame_size(HBC *hbc, u32 function_id, u32 *out);
/**
* Get a string by index.
*/
+1 -1
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_ENCODER_H
#define LIBHBC_ENCODER_H
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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 the literal data in the SLP pool (0 if unknown) */
u32 paddr; /* absolute file offset of array data or object values (0 if unknown) */
char *formatted; /* cached JS literal text; NULL if not yet formatted */
u32 *xref_addrs; /* instruction addresses that construct this literal */
u32 xref_count;
} HBCLiteralEntry;
typedef bool (*HBCLiteralKeyCallback)(const char *key, void *user);
typedef bool(*HBCLiteralKeyCallback)(const char *key, void *user);
/* Format a literal directly from raw parameters. Does not touch the cache.
* Returned string is heap-allocated; caller frees with free (). */
+1 -1
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_HERMES_OPCODES_H
#define LIBHBC_HERMES_OPCODES_H
+3 -2
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef LIBHBC_FILE_PARSER_H
#define LIBHBC_FILE_PARSER_H
@@ -213,7 +213,8 @@ struct BytecodeModule {
/* Main HBC reader structure */
struct HBCReader {
/* File data */
BufferReader file_buffer;
RBuffer *file_buffer;
u8 *file_bytes_cache;
/* Parsed header and tables */
HBCHeader header;
+72 -16
View File
@@ -1,7 +1,7 @@
project(
'r2hermes',
'c',
version: '1.4.2',
version: '1.6.0',
license: 'MIT',
default_options: [
'c_std=c11',
@@ -39,11 +39,67 @@ 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 = files(
'src/lib/utils/string_buffer.c',
'src/lib/utils/buffer_reader.c',
)
lib_utils_src = []
lib_parsers_src = files(
'src/lib/parsers/hbc_file_parser.c',
@@ -79,6 +135,7 @@ libhbc = static_library(
lib_src,
include_directories: include_dirs,
c_args: ['-pedantic'],
dependencies: libhbc_compile_deps,
install: true,
)
@@ -86,6 +143,7 @@ libhbc = static_library(
libhbc_dep = declare_dependency(
link_with: libhbc,
include_directories: include_dirs,
dependencies: libhbc_link_deps,
)
# Install library headers
@@ -93,40 +151,38 @@ install_subdir('include/hbc',
install_dir: 'include',
)
# libhbctool CLI
libhbctool_src = files('src/tool/libhbctool.c')
# r2hermes CLI
r2hermes_src = files('src/tool/r2hermes.c')
executable(
'libhbctool',
libhbctool_src,
'r2hermes',
r2hermes_src,
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 = run_command('which', 'r2', check: false).returncode() == 0
r2_found = r2_bin.found()
endif
if r2_plugdir_opt != ''
r2_plugdir = r2_plugdir_opt
elif r2_found
r2_plugdir = run_command('r2', '-H', 'R2_USER_PLUGINS', check: false).stdout().strip()
r2_plugdir = run_command(r2_bin, '-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', '-H', 'R2_PREFIX', check: false).stdout().strip()
r2_prefix = run_command(r2_bin, '-H', 'R2_PREFIX', check: false).stdout().strip()
else
r2_prefix = r2_prefix_opt
endif
@@ -184,7 +240,7 @@ endif
# Summary
message('r2hermes build summary:')
message(' Library: libhbc')
message(' CLI: libhbctool')
message(' CLI: r2hermes')
if r2_core.found()
message(' radare2 plugins: core_hbc (includes arch, bin, asm)')
message(' Plugin install path: ' + r2_plugdir)
+1 -1
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifdef R2_CORE_H
extern RCorePlugin r_core_plugin_r2hermes;
+24 -3
View File
@@ -1,5 +1,26 @@
HBC_CORE_WD=$(LIBR)/xps/p/r2hermes
CFLAGS+=-I$(HBC_CORE_WD)/include
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')
HBC_CORE_OBJ=$(HBC_CORE_WD)/src/r2/core_hbc_one.o
LDFLAGS+=$(HBC_CORE_WD)/build/libhbc.a
EXTERNAL_STATIC_OBJS+=$(HBC_CORE_OBJ)
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 $@ $<
-2
View File
@@ -8,8 +8,6 @@ core_plugins += ['r2hermes']
r_core_additional_inc += include_directories(join_paths('..', 'include'))
r_core_additional_sources += files(
'../src/lib/utils/string_buffer.c',
'../src/lib/utils/buffer_reader.c',
'../src/lib/parsers/hbc_file_parser.c',
'../src/lib/parsers/hbc_bytecode_parser.c',
'../src/lib/decompilation/translator.c',
+1 -1
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifdef R2_CORE_H
#include "core/deps.h"
File diff suppressed because it is too large. Load diff
+50 -50
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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(StringBuffer *out, const char *s) {
RETURN_IF_ERROR (_hbc_sb_append (out, "\""));
static Result append_quoted(RStrBuf *out, const char *s) {
RETURN_IF_ERROR (r_strbuf_append (out, "\""));
for (const char *p = s; p && *p; p++) {
unsigned char c = (unsigned char)*p;
if (c == '"' || c == '\\') {
RETURN_IF_ERROR (_hbc_sb_append_char (out, '\\'));
RETURN_IF_ERROR (_hbc_sb_append_char (out, (char)c));
RETURN_IF_ERROR (r_strbuf_appendf (out, "%c", '\\'));
RETURN_IF_ERROR (r_strbuf_appendf (out, "%c", (char)c));
} else if (c < 0x20) {
RETURN_IF_ERROR (_hbc_sb_appendf (out, "\\x%02x", c));
RETURN_IF_ERROR (r_strbuf_appendf (out, "\\x%02x", c));
} else {
RETURN_IF_ERROR (_hbc_sb_append_char (out, (char)c));
RETURN_IF_ERROR (r_strbuf_appendf (out, "%c", (char)c));
}
}
return _hbc_sb_append (out, "\"");
return HBC_TO_RESULT (r_strbuf_append (out, "\""));
}
Result _hbc_format_property_from_string_id(HBCReader *r, u32 string_id, StringBuffer *out) {
Result _hbc_format_property_from_string_id(HBCReader *r, u32 string_id, RStrBuf *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 (_hbc_sb_append (out, "."));
return _hbc_sb_append (out, 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_IF_ERROR (r_strbuf_append (out, "["));
if (s) {
RETURN_IF_ERROR (append_quoted (out, s));
} else {
RETURN_IF_ERROR (_hbc_sb_append (out, "\"\""));
RETURN_IF_ERROR (r_strbuf_append (out, "\"\""));
}
RETURN_IF_ERROR (_hbc_sb_append (out, "]"));
RETURN_IF_ERROR (r_strbuf_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, StringBuffer *out) {
static Result slp_append_value_js(HBCReader *r, const SLPValue *v, bool for_key, RStrBuf *out) {
switch (v->tag) {
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_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_IntegerTag:
{
return _hbc_sb_appendf (out, "%u", (unsigned)v->v.intv);
return HBC_TO_RESULT (r_strbuf_appendf (out, "%u", (unsigned)v->v.intv));
}
case SLP_NumberTag:
{
/* Use %.17g to preserve precision while avoiding trailing noise */
return _hbc_sb_appendf (out, "%.17g", v->v.num);
return HBC_TO_RESULT (r_strbuf_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_sb_append (out, s);
return HBC_TO_RESULT (r_strbuf_append (out, s));
}
if (s) {
return append_quoted (out, s);
}
return _hbc_sb_append (out, "\"\"");
return HBC_TO_RESULT (r_strbuf_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, StringBuffer *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, RStrBuf *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_sb_append (out, "}");
return HBC_TO_RESULT (r_strbuf_append (out, "}"));
}
return _hbc_sb_appendf (out, " /*shape:%u vals:%u*/ }", key_count, value_count);
return HBC_TO_RESULT (r_strbuf_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 (_hbc_sb_append (out, multiline? "{\n": "{"));
RETURN_IF_ERROR (r_strbuf_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_sb_append (out, "}");
return HBC_TO_RESULT (r_strbuf_append (out, "}"));
}
return _hbc_sb_appendf (out, " /*k:%u id:%u vals:%u*/ }", key_count, keys_id, values_id);
return HBC_TO_RESULT (r_strbuf_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_sb_append (out, "}");
return HBC_TO_RESULT (r_strbuf_append (out, "}"));
}
return _hbc_sb_appendf (out, " /*k:%u id:%u vals:%u*/ }", key_count, keys_id, values_id);
return HBC_TO_RESULT (r_strbuf_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 (_hbc_sb_append (out, " "));
RETURN_IF_ERROR (r_strbuf_append (out, " "));
}
if (!multiline && i) {
RETURN_IF_ERROR (_hbc_sb_append (out, ", "));
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (out, ": "));
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (out, i + 1 < n? ",\n": "\n"));
RETURN_IF_ERROR (r_strbuf_append (out, i + 1 < n? ",\n": "\n"));
}
}
free (key_vals);
free (val_vals);
RETURN_IF_ERROR (_hbc_sb_append (out, multiline? "}": "}"));
RETURN_IF_ERROR (r_strbuf_append (out, multiline? "}": "}"));
return SUCCESS_RESULT ();
}
Result _hbc_format_array_literal(HBCReader *r, u32 value_count, u32 array_id, StringBuffer *out, LiteralsPrettyPolicy policy, bool suppress_comments) {
Result _hbc_format_array_literal(HBCReader *r, u32 value_count, u32 array_id, RStrBuf *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 (_hbc_sb_append (out, multiline? "[\n": "["));
RETURN_IF_ERROR (r_strbuf_append (out, multiline? "[\n": "["));
if (!r || !r->arrays || value_count == 0) {
if (suppress_comments) {
return _hbc_sb_append (out, "]");
return HBC_TO_RESULT (r_strbuf_append (out, "]"));
}
return _hbc_sb_appendf (out, " /*n:%u id:%u*/ ]", value_count, array_id);
return HBC_TO_RESULT (r_strbuf_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, St
if (!ok) {
free (vals);
if (suppress_comments) {
return _hbc_sb_append (out, "]");
return HBC_TO_RESULT (r_strbuf_append (out, "]"));
}
return _hbc_sb_appendf (out, " /*n:%u id:%u*/ ]", value_count, array_id);
return HBC_TO_RESULT (r_strbuf_appendf (out, " /*n:%u id:%u*/ ]", value_count, array_id));
}
for (u32 i = 0; i < value_count; i++) {
if (multiline) {
RETURN_IF_ERROR (_hbc_sb_append (out, " "));
RETURN_IF_ERROR (r_strbuf_append (out, " "));
}
if (!multiline && i) {
RETURN_IF_ERROR (_hbc_sb_append (out, ", "));
RETURN_IF_ERROR (r_strbuf_append (out, ", "));
}
RETURN_IF_ERROR (slp_append_value_js (r, &vals[i], false, out));
if (multiline) {
RETURN_IF_ERROR (_hbc_sb_append (out, i + 1 < value_count? ",\n": "\n"));
RETURN_IF_ERROR (r_strbuf_append (out, i + 1 < value_count? ",\n": "\n"));
}
}
free (vals);
RETURN_IF_ERROR (_hbc_sb_append (out, "]"));
RETURN_IF_ERROR (r_strbuf_append (out, "]"));
return SUCCESS_RESULT ();
}
Result _hbc_format_variadic_call(const ParsedInstruction *insn, StringBuffer *out) {
Result _hbc_format_variadic_call(const ParsedInstruction *insn, RStrBuf *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 (_hbc_sb_append (out, ", "));
RETURN_IF_ERROR (_hbc_sb_appendf (out, "r%u", base + i));
RETURN_IF_ERROR (r_strbuf_append (out, ", "));
RETURN_IF_ERROR (r_strbuf_appendf (out, "r%u", base + i));
}
/* Also annotate argc */
RETURN_IF_ERROR (_hbc_sb_appendf (out, " /*argc:%u*/", argc));
RETURN_IF_ERROR (r_strbuf_appendf (out, " /*argc:%u*/", argc));
} else {
/* No args beyond 'this' */
}
+176 -166
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <hbc/decompilation/token.h>
#include <hbc/decompilation/decompiler.h>
@@ -6,8 +6,8 @@
#include <ctype.h>
static Token *alloc_token(TokenType type, size_t extra) {
Token *t = (Token *)malloc ((sizeof (Token)) + extra);
static HbcToken *alloc_token(HbcTokenType type, size_t extra) {
HbcToken *t = (HbcToken *)malloc ((sizeof (HbcToken)) + extra);
if (!t) {
return NULL;
}
@@ -16,7 +16,7 @@ static Token *alloc_token(TokenType type, size_t extra) {
return t;
}
Result _hbc_token_string_init(TokenString *ts, ParsedInstruction *insn) {
Result _hbc_token_string_init(HbcTokenString *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(TokenString *ts, ParsedInstruction *insn) {
return SUCCESS_RESULT ();
}
void _hbc_token_string_cleanup(TokenString *ts) {
void _hbc_token_string_cleanup(HbcTokenString *ts) {
if (!ts) {
return;
}
Token *cur = ts->head;
HbcToken *cur = ts->head;
while (cur) {
Token *nxt = cur->next;
HbcToken *nxt = cur->next;
_hbc_token_free (cur);
cur = nxt;
}
@@ -39,7 +39,7 @@ void _hbc_token_string_cleanup(TokenString *ts) {
ts->assembly = NULL;
}
Result _hbc_token_string_add_token(TokenString *ts, Token *t) {
Result _hbc_token_string_add_token(HbcTokenString *ts, HbcToken *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(TokenString *ts, Token *t) {
return SUCCESS_RESULT ();
}
Token *create_raw_token(const char *text) {
HbcToken *hbc_token_new_raw(const char *text) {
if (!text) {
return NULL;
}
RawToken *rt = (RawToken *)alloc_token (TOKEN_TYPE_RAW, sizeof (RawToken) - sizeof (Token));
HbcRawToken *rt = (HbcRawToken *)alloc_token (HBC_TOKEN_TYPE_RAW, sizeof (HbcRawToken) - sizeof (HbcToken));
if (!rt) {
return NULL;
}
@@ -67,59 +67,59 @@ Token *create_raw_token(const char *text) {
return NULL;
}
memcpy (rt->text, text, len + 1);
return (Token *)rt;
return (HbcToken *)rt;
}
Token *create_left_hand_reg_token(int reg_num) {
LeftHandRegToken *t = (LeftHandRegToken *)alloc_token (TOKEN_TYPE_LEFT_HAND_REG, sizeof (LeftHandRegToken) - sizeof (Token));
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));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
return (Token *)t;
return (HbcToken *)t;
}
Token *create_right_hand_reg_token(int reg_num) {
RightHandRegToken *t = (RightHandRegToken *)alloc_token (TOKEN_TYPE_RIGHT_HAND_REG, sizeof (RightHandRegToken) - sizeof (Token));
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));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
return (Token *)t;
return (HbcToken *)t;
}
Token *create_assignment_token(void) {
return alloc_token (TOKEN_TYPE_ASSIGNMENT, 0);
HbcToken *hbc_token_new_assignment(void) {
return alloc_token (HBC_TOKEN_TYPE_ASSIGNMENT, 0);
}
Token *create_left_parenthesis_token(void) {
return alloc_token (TOKEN_TYPE_LEFT_PARENTHESIS, 0);
HbcToken *hbc_token_new_left_parenthesis(void) {
return alloc_token (HBC_TOKEN_TYPE_LEFT_PARENTHESIS, 0);
}
Token *create_right_parenthesis_token(void) {
return alloc_token (TOKEN_TYPE_RIGHT_PARENTHESIS, 0);
HbcToken *hbc_token_new_right_parenthesis(void) {
return alloc_token (HBC_TOKEN_TYPE_RIGHT_PARENTHESIS, 0);
}
Token *create_dot_accessor_token(void) {
return alloc_token (TOKEN_TYPE_DOT_ACCESSOR, 0);
HbcToken *hbc_token_new_dot_accessor(void) {
return alloc_token (HBC_TOKEN_TYPE_DOT_ACCESSOR, 0);
}
Token *create_bind_token(int reg_num) {
BindToken *t = (BindToken *)alloc_token (TOKEN_TYPE_BIND, sizeof (BindToken) - sizeof (Token));
HbcToken *hbc_token_new_bind(int reg_num) {
HbcBindToken *t = (HbcBindToken *)alloc_token (HBC_TOKEN_TYPE_BIND, sizeof (HbcBindToken) - sizeof (HbcToken));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
return (Token *)t;
return (HbcToken *)t;
}
Token *create_return_directive_token(void) {
return alloc_token (TOKEN_TYPE_RETURN_DIRECTIVE, 0);
HbcToken *hbc_token_new_return_directive(void) {
return alloc_token (HBC_TOKEN_TYPE_RETURN_DIRECTIVE, 0);
}
Token *create_throw_directive_token(void) {
return alloc_token (TOKEN_TYPE_THROW_DIRECTIVE, 0);
HbcToken *hbc_token_new_throw_directive(void) {
return alloc_token (HBC_TOKEN_TYPE_THROW_DIRECTIVE, 0);
}
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));
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));
if (!t) {
return NULL;
}
@@ -131,69 +131,69 @@ Token *create_function_table_index_token(u32 function_id, struct HermesDecompile
t->is_generator = false;
t->is_async = false;
t->parent_environment = NULL;
return (Token *)t;
return (HbcToken *)t;
}
Token *create_jump_condition_token(u32 target_address) {
JumpConditionToken *t = (JumpConditionToken *)alloc_token (TOKEN_TYPE_JUMP_CONDITION, sizeof (JumpConditionToken) - sizeof (Token));
HbcToken *hbc_token_new_jump_condition(u32 target_address) {
HbcJumpConditionToken *t = (HbcJumpConditionToken *)alloc_token (HBC_TOKEN_TYPE_JUMP_CONDITION, sizeof (HbcJumpConditionToken) - sizeof (HbcToken));
if (!t) {
return NULL;
}
t->target_address = target_address;
return (Token *)t;
return (HbcToken *)t;
}
Token *create_jump_not_condition_token(u32 target_address) {
JumpNotConditionToken *t = (JumpNotConditionToken *)alloc_token (TOKEN_TYPE_JUMP_NOT_CONDITION, sizeof (JumpNotConditionToken) - sizeof (Token));
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));
if (!t) {
return NULL;
}
t->target_address = target_address;
return (Token *)t;
return (HbcToken *)t;
}
Token *create_get_environment_token(int reg_num, int nesting_level) {
GetEnvironmentToken *t = (GetEnvironmentToken *)alloc_token (TOKEN_TYPE_GET_ENVIRONMENT, sizeof (GetEnvironmentToken) - sizeof (Token));
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));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
t->nesting_level = nesting_level;
return (Token *)t;
return (HbcToken *)t;
}
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));
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));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
t->slot_index = slot_index;
return (Token *)t;
return (HbcToken *)t;
}
Token *create_new_environment_token(int reg_num) {
NewEnvironmentToken *t = (NewEnvironmentToken *)alloc_token (TOKEN_TYPE_NEW_ENVIRONMENT, sizeof (NewEnvironmentToken) - sizeof (Token));
HbcToken *hbc_token_new_new_environment(int reg_num) {
HbcNewEnvironmentToken *t = (HbcNewEnvironmentToken *)alloc_token (HBC_TOKEN_TYPE_NEW_ENVIRONMENT, sizeof (HbcNewEnvironmentToken) - sizeof (HbcToken));
if (!t) {
return NULL;
}
t->reg_num = reg_num;
return (Token *)t;
return (HbcToken *)t;
}
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));
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));
if (!t) {
return NULL;
}
t->dest_register = dest_register;
t->parent_register = parent_register;
t->number_of_slots = number_of_slots;
return (Token *)t;
return (HbcToken *)t;
}
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));
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));
if (!t) {
return NULL;
}
@@ -202,22 +202,22 @@ Token *create_switch_imm_token(int value_reg, u32 jump_table_address, u32 defaul
t->default_jump_address = default_jump_address;
t->unsigned_min_value = unsigned_min_value;
t->unsigned_max_value = unsigned_max_value;
return (Token *)t;
return (HbcToken *)t;
}
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));
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));
if (!t) {
return NULL;
}
t->env_register = env_register;
t->slot_index = slot_index;
t->value_register = value_register;
return (Token *)t;
return (HbcToken *)t;
}
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));
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));
if (!t) {
return NULL;
}
@@ -225,11 +225,11 @@ Token *create_for_in_loop_init_token(int obj_props_register, int obj_register, i
t->obj_register = obj_register;
t->iter_index_register = iter_index_register;
t->iter_size_register = iter_size_register;
return (Token *)t;
return (HbcToken *)t;
}
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));
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));
if (!t) {
return NULL;
}
@@ -238,217 +238,227 @@ Token *create_for_in_loop_next_iter_token(int next_value_register, int obj_props
t->obj_register = obj_register;
t->iter_index_register = iter_index_register;
t->iter_size_register = iter_size_register;
return (Token *)t;
return (HbcToken *)t;
}
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));
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));
if (!t) {
return NULL;
}
t->result_out_reg = result_out_reg;
t->return_bool_out_reg = return_bool_out_reg;
return (Token *)t;
return (HbcToken *)t;
}
Token *create_save_generator_token(u32 address) {
SaveGeneratorToken *t = (SaveGeneratorToken *)alloc_token (TOKEN_TYPE_SAVE_GENERATOR, sizeof (SaveGeneratorToken) - sizeof (Token));
HbcToken *hbc_token_new_save_generator(u32 address) {
HbcSaveGeneratorToken *t = (HbcSaveGeneratorToken *)alloc_token (HBC_TOKEN_TYPE_SAVE_GENERATOR, sizeof (HbcSaveGeneratorToken) - sizeof (HbcToken));
if (!t) {
return NULL;
}
t->address = address;
return (Token *)t;
return (HbcToken *)t;
}
Token *create_start_generator_token(void) {
return alloc_token (TOKEN_TYPE_START_GENERATOR, 0);
HbcToken *hbc_token_new_start_generator(void) {
return alloc_token (HBC_TOKEN_TYPE_START_GENERATOR, 0);
}
Token *create_catch_block_start_token(int arg_register) {
CatchBlockStartToken *t = (CatchBlockStartToken *)alloc_token (TOKEN_TYPE_CATCH_BLOCK_START, sizeof (CatchBlockStartToken) - sizeof (Token));
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));
if (!t) {
return NULL;
}
t->arg_register = arg_register;
return (Token *)t;
return (HbcToken *)t;
}
void _hbc_token_free(Token *tok) {
void _hbc_token_free(HbcToken *tok) {
if (!tok) {
return;
}
if (tok->type == TOKEN_TYPE_RAW) {
RawToken *rt = (RawToken *)tok;
if (tok->type == HBC_TOKEN_TYPE_RAW) {
HbcRawToken *rt = (HbcRawToken *)tok;
free (rt->text);
}
free (tok);
}
static Result append_reg(StringBuffer *b, int r) {
return _hbc_sb_appendf (b, "r%d", r);
static Result append_reg(RStrBuf *b, int r) {
return HBC_TO_RESULT (r_strbuf_appendf (b, "r%d", r));
}
/* Very simple token to string printer, good enough for M1 */
Result _hbc_token_to_string(Token *token, StringBuffer *buffer) {
Result _hbc_token_to_string(HbcToken *token, RStrBuf *buffer) {
if (!token || !buffer) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "_hbc_token_to_string: NULL");
}
switch (token->type) {
case TOKEN_TYPE_RAW:
case HBC_TOKEN_TYPE_RAW:
{
RawToken *t = (RawToken *)token;
return _hbc_sb_append (buffer, t->text? t->text: "");
HbcRawToken *t = (HbcRawToken *)token;
return HBC_TO_RESULT (r_strbuf_append (buffer, t->text? t->text: ""));
}
case TOKEN_TYPE_LEFT_HAND_REG:
case HBC_TOKEN_TYPE_LEFT_HAND_REG:
{
LeftHandRegToken *t = (LeftHandRegToken *)token;
HbcLeftHandRegToken *t = (HbcLeftHandRegToken *)token;
return append_reg (buffer, t->reg_num);
}
case TOKEN_TYPE_RIGHT_HAND_REG:
case HBC_TOKEN_TYPE_RIGHT_HAND_REG:
{
RightHandRegToken *t = (RightHandRegToken *)token;
HbcRightHandRegToken *t = (HbcRightHandRegToken *)token;
return append_reg (buffer, t->reg_num);
}
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:
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:
{
BindToken *t = (BindToken *)token;
RETURN_IF_ERROR (_hbc_sb_append (buffer, ".bind("));
HbcBindToken *t = (HbcBindToken *)token;
RETURN_IF_ERROR (r_strbuf_append (buffer, ".bind("));
RETURN_IF_ERROR (append_reg (buffer, t->reg_num));
return _hbc_sb_append (buffer, ")");
return HBC_TO_RESULT (r_strbuf_append (buffer, ")"));
}
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:
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:
{
FunctionTableIndexToken *t = (FunctionTableIndexToken *)token;
HbcFunctionTableIndexToken *t = (HbcFunctionTableIndexToken *)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 (_hbc_sb_append (buffer, name));
RETURN_IF_ERROR (r_strbuf_append (buffer, name));
return SUCCESS_RESULT ();
}
}
return _hbc_sb_appendf (buffer, "fn_%u", t->function_id);
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "fn_%u", t->function_id));
}
case TOKEN_TYPE_JUMP_CONDITION:
case HBC_TOKEN_TYPE_JUMP_CONDITION:
{
JumpConditionToken *t = (JumpConditionToken *)token;
return _hbc_sb_appendf (buffer, "/* jump -> 0x%08x */", t->target_address);
HbcJumpConditionToken *t = (HbcJumpConditionToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "/* jump -> 0x%08x */", t->target_address));
}
case TOKEN_TYPE_JUMP_NOT_CONDITION:
case HBC_TOKEN_TYPE_JUMP_NOT_CONDITION:
{
JumpNotConditionToken *t = (JumpNotConditionToken *)token;
return _hbc_sb_appendf (buffer, "/* jump_if_not -> 0x%08x */", t->target_address);
HbcJumpNotConditionToken *t = (HbcJumpNotConditionToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "/* jump_if_not -> 0x%08x */", t->target_address));
}
case TOKEN_TYPE_GET_ENVIRONMENT:
case HBC_TOKEN_TYPE_GET_ENVIRONMENT:
{
GetEnvironmentToken *t = (GetEnvironmentToken *)token;
RETURN_IF_ERROR (_hbc_sb_append (buffer, "get_env("));
HbcGetEnvironmentToken *t = (HbcGetEnvironmentToken *)token;
RETURN_IF_ERROR (r_strbuf_append (buffer, "get_env("));
RETURN_IF_ERROR (append_reg (buffer, t->reg_num));
RETURN_IF_ERROR (_hbc_sb_append (buffer, ", "));
RETURN_IF_ERROR (_hbc_sb_appendf (buffer, "%d", t->nesting_level));
return _hbc_sb_append (buffer, ")");
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, ")"));
}
case TOKEN_TYPE_LOAD_FROM_ENVIRONMENT:
case HBC_TOKEN_TYPE_LOAD_FROM_ENVIRONMENT:
{
LoadFromEnvironmentToken *t = (LoadFromEnvironmentToken *)token;
RETURN_IF_ERROR (_hbc_sb_append (buffer, "env_load("));
HbcLoadFromEnvironmentToken *t = (HbcLoadFromEnvironmentToken *)token;
RETURN_IF_ERROR (r_strbuf_append (buffer, "env_load("));
RETURN_IF_ERROR (append_reg (buffer, t->reg_num));
RETURN_IF_ERROR (_hbc_sb_append (buffer, ", "));
RETURN_IF_ERROR (_hbc_sb_appendf (buffer, "%d", t->slot_index));
return _hbc_sb_append (buffer, ")");
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, ")"));
}
case TOKEN_TYPE_NEW_ENVIRONMENT:
case HBC_TOKEN_TYPE_NEW_ENVIRONMENT:
{
NewEnvironmentToken *t = (NewEnvironmentToken *)token;
RETURN_IF_ERROR (_hbc_sb_append (buffer, "new_env("));
HbcNewEnvironmentToken *t = (HbcNewEnvironmentToken *)token;
RETURN_IF_ERROR (r_strbuf_append (buffer, "new_env("));
RETURN_IF_ERROR (append_reg (buffer, t->reg_num));
return _hbc_sb_append (buffer, ")");
return HBC_TO_RESULT (r_strbuf_append (buffer, ")"));
}
case TOKEN_TYPE_NEW_INNER_ENVIRONMENT:
case HBC_TOKEN_TYPE_NEW_INNER_ENVIRONMENT:
{
NewInnerEnvironmentToken *t = (NewInnerEnvironmentToken *)token;
return _hbc_sb_appendf (buffer,
HbcNewInnerEnvironmentToken *t = (HbcNewInnerEnvironmentToken *)token;
return HBC_TO_RESULT (r_strbuf_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 TOKEN_TYPE_SWITCH_IMM:
case HBC_TOKEN_TYPE_SWITCH_IMM:
{
SwitchImmToken *t = (SwitchImmToken *)token;
return _hbc_sb_appendf (buffer,
HbcSwitchImmToken *t = (HbcSwitchImmToken *)token;
return HBC_TO_RESULT (r_strbuf_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 TOKEN_TYPE_STORE_TO_ENVIRONMENT:
case HBC_TOKEN_TYPE_STORE_TO_ENVIRONMENT:
{
StoreToEnvironmentToken *t = (StoreToEnvironmentToken *)token;
return _hbc_sb_appendf (buffer,
HbcStoreToEnvironmentToken *t = (HbcStoreToEnvironmentToken *)token;
return HBC_TO_RESULT (r_strbuf_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 TOKEN_TYPE_FOR_IN_LOOP_INIT:
case HBC_TOKEN_TYPE_FOR_IN_LOOP_INIT:
{
ForInLoopInitToken *t = (ForInLoopInitToken *)token;
return _hbc_sb_appendf (buffer,
HbcForInLoopInitToken *t = (HbcForInLoopInitToken *)token;
return HBC_TO_RESULT (r_strbuf_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 TOKEN_TYPE_FOR_IN_LOOP_NEXT_ITER:
case HBC_TOKEN_TYPE_FOR_IN_LOOP_NEXT_ITER:
{
ForInLoopNextIterToken *t = (ForInLoopNextIterToken *)token;
return _hbc_sb_appendf (buffer,
HbcForInLoopNextIterToken *t = (HbcForInLoopNextIterToken *)token;
return HBC_TO_RESULT (r_strbuf_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 TOKEN_TYPE_RESUME_GENERATOR:
case HBC_TOKEN_TYPE_RESUME_GENERATOR:
{
ResumeGeneratorToken *t = (ResumeGeneratorToken *)token;
return _hbc_sb_appendf (buffer, "resume_gen(r%d, r%d)", t->result_out_reg, t->return_bool_out_reg);
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));
}
case TOKEN_TYPE_SAVE_GENERATOR:
case HBC_TOKEN_TYPE_SAVE_GENERATOR:
{
SaveGeneratorToken *t = (SaveGeneratorToken *)token;
return _hbc_sb_appendf (buffer, "save_gen(0x%08x)", t->address);
HbcSaveGeneratorToken *t = (HbcSaveGeneratorToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "save_gen(0x%08x)", t->address));
}
case TOKEN_TYPE_START_GENERATOR: return _hbc_sb_append (buffer, "start_generator()");
case TOKEN_TYPE_CATCH_BLOCK_START:
case HBC_TOKEN_TYPE_START_GENERATOR: return HBC_TO_RESULT (r_strbuf_append (buffer, "start_generator()"));
case HBC_TOKEN_TYPE_CATCH_BLOCK_START:
{
CatchBlockStartToken *t = (CatchBlockStartToken *)token;
return _hbc_sb_appendf (buffer, "catch(r%d)", t->arg_register);
HbcCatchBlockStartToken *t = (HbcCatchBlockStartToken *)token;
return HBC_TO_RESULT (r_strbuf_appendf (buffer, "catch(r%d)", t->arg_register));
}
default:
return _hbc_sb_append (buffer, "/*token*/");
return HBC_TO_RESULT (r_strbuf_append (buffer, "/*token*/"));
}
}
Result _hbc_token_string_to_string(TokenString *ts, StringBuffer *out) {
Result _hbc_token_string_to_string(HbcTokenString *ts, RStrBuf *out) {
if (!ts || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "_hbc_token_string_to_string: NULL");
}
Token *cur = ts->head;
TokenType prev = (TokenType) (-1);
HbcToken *cur = ts->head;
HbcTokenType prev = (HbcTokenType) (-1);
while (cur) {
/* spacing rules: space before '(', none after '(' or '.' nor before ')' or '.' */
if (cur != ts->head) {
bool need_space;
if (cur->type == TOKEN_TYPE_LEFT_PARENTHESIS) {
if (cur->type == HBC_TOKEN_TYPE_LEFT_PARENTHESIS) {
need_space = true;
} else if (cur->type == TOKEN_TYPE_RIGHT_PARENTHESIS || cur->type == TOKEN_TYPE_DOT_ACCESSOR) {
} else if (cur->type == HBC_TOKEN_TYPE_RIGHT_PARENTHESIS || cur->type == HBC_TOKEN_TYPE_DOT_ACCESSOR) {
need_space = false;
} else {
need_space = prev != TOKEN_TYPE_LEFT_PARENTHESIS && prev != TOKEN_TYPE_DOT_ACCESSOR;
need_space = prev != HBC_TOKEN_TYPE_LEFT_PARENTHESIS && prev != HBC_TOKEN_TYPE_DOT_ACCESSOR;
}
if (need_space) {
RETURN_IF_ERROR (_hbc_sb_append_char (out, ' '));
RETURN_IF_ERROR (r_strbuf_appendf (out, "%c", ' '));
}
}
RETURN_IF_ERROR (_hbc_token_to_string (cur, out));
File diff suppressed because it is too large. Load diff
+244 -99
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <hbc/hbc.h>
#include <hbc/common.h>
@@ -15,19 +15,40 @@
#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 = (HBC *)calloc (1, sizeof (HBC));
if (!hbc) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate HBC");
}
HBC *hbc = R_NEW0 (HBC);
Result res = _hbc_reader_init (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
@@ -51,10 +72,7 @@ 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 = (HBC *)calloc (1, sizeof (HBC));
if (!hbc) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate HBC");
}
HBC *hbc = R_NEW0 (HBC);
Result res = _hbc_reader_init (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
@@ -62,70 +80,48 @@ Result hbc_open_from_memory(const u8 *data, size_t size, HBC **out) {
return res;
}
res = _hbc_buffer_reader_init_from_memory (&hbc->reader.file_buffer, data, size);
if (res.code != RESULT_SUCCESS) {
hbc->reader.file_buffer = r_buf_new_with_bytes (data, size);
if (!hbc->reader.file_buffer) {
_hbc_reader_cleanup (&hbc->reader);
free (hbc);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate buffer");
}
res = hbc_parse_reader (hbc);
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);
if (res.code != RESULT_SUCCESS) {
free (hbc);
return res;
}
/* Parse header and all sections */
res = _hbc_reader_read_header (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
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_functions_robust (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
res = _hbc_reader_read_string_kinds (&hbc->reader);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
}
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);
res = hbc_parse_reader (hbc);
if (res.code != RESULT_SUCCESS) {
hbc_close (hbc);
return res;
@@ -140,6 +136,7 @@ void hbc_close(HBC *hbc) {
return;
}
hbc_literals_reset (hbc);
free (hbc->function_flags);
_hbc_reader_cleanup (&hbc->reader);
free (hbc);
}
@@ -166,6 +163,89 @@ Result hbc_get_header(HBC *hbc, HBCHeader *out) {
return SUCCESS_RESULT ();
}
static bool opcode_is(const Instruction *insn, const char *name, const char *long_name) {
const char *mnemonic = insn? insn->name: NULL;
return mnemonic && (!strcmp (mnemonic, name) || !strcmp (mnemonic, long_name));
}
static void add_function_edge(RVecHBCFunctionEdge *edges, u32 source, u32 target) {
HBCFunctionEdge edge = { .source = source, .target = target };
RVecHBCFunctionEdge_push_back (edges, &edge);
}
static void hbc_ensure_function_flags(HBC *hbc) {
if (!hbc || hbc->function_flags_ready) {
return;
}
hbc->function_flags_ready = true;
const u32 count = hbc->reader.header.functionCount;
if (!count) {
return;
}
hbc->function_flags = (u32 *)calloc (count, sizeof (u32));
if (!hbc->function_flags) {
return;
}
RVecHBCFunctionEdge edges;
RVecHBCFunctionEdge_init (&edges);
const HBCISA isa = hbc_isa_getv (hbc->reader.header.version);
for (u32 function_id = 0; function_id < count; function_id++) {
const u8 *code = NULL;
u32 size = 0;
if (hbc_get_function_bytecode (hbc, function_id, &code, &size).code != RESULT_SUCCESS || !code) {
continue;
}
for (u32 pc = 0; pc < size;) {
const u8 opcode = code[pc];
const Instruction *insn = opcode < isa.count? &isa.instructions[opcode]: NULL;
if (!insn || !insn->name || !insn->binary_size) {
break;
}
u32 values[6];
const size_t decoded_size = decode_operands (insn, code + pc, size - pc, values);
if (!decoded_size) {
break;
}
const u32 target = values[2];
if (target >= count) {
pc += (u32)decoded_size;
continue;
}
if (opcode_is (insn, "create_async_closure", "create_async_closure_long_index")) {
hbc->function_flags[target] |= HBC_FUNCTION_ASYNC;
} else if (opcode_is (insn, "create_generator_closure", "create_generator_closure_long_index")) {
hbc->function_flags[target] |= HBC_FUNCTION_GENERATOR;
add_function_edge (&edges, function_id, target);
} else if (opcode_is (insn, "create_generator", "create_generator_long_index")) {
hbc->function_flags[function_id] |= HBC_FUNCTION_GENERATOR;
hbc->function_flags[target] |= HBC_FUNCTION_GENERATOR;
add_function_edge (&edges, function_id, target);
}
pc += (u32)decoded_size;
}
}
bool changed;
do {
changed = false;
HBCFunctionEdge *edge;
R_VEC_FOREACH (&edges, edge) {
if ((hbc->function_flags[edge->source] & HBC_FUNCTION_ASYNC) &&
! (hbc->function_flags[edge->target] & HBC_FUNCTION_ASYNC)) {
hbc->function_flags[edge->target] |= HBC_FUNCTION_ASYNC;
changed = true;
}
}
} while (changed);
for (u32 i = 0; i < count; i++) {
if (hbc->function_flags[i] & HBC_FUNCTION_ASYNC) {
hbc->function_flags[i] &= ~HBC_FUNCTION_GENERATOR;
}
}
RVecHBCFunctionEdge_fini (&edges);
}
Result hbc_get_function_info(HBC *hbc, u32 function_id, HBCFunc *out) {
if (!hbc || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
@@ -186,6 +266,57 @@ Result hbc_get_function_info(HBC *hbc, u32 function_id, HBCFunc *out) {
out->offset = fh->offset;
out->size = fh->bytecodeSizeInBytes;
out->param_count = fh->paramCount;
hbc_ensure_function_flags (hbc);
out->flags = hbc->function_flags? hbc->function_flags[function_id]: HBC_FUNCTION_NONE;
return SUCCESS_RESULT ();
}
Result hbc_get_function_exception_handlers(HBC *hbc, u32 function_id, HBCExceptionHandlerArray *out) {
if (!hbc || !out) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
}
out->handlers = NULL;
out->count = 0;
HBCReader *reader = &hbc->reader;
if (!reader->function_headers || function_id >= reader->header.functionCount) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Function ID out of range");
}
if (!reader->function_id_to_exc_handlers) {
return SUCCESS_RESULT ();
}
const ExceptionHandlerList *source = &reader->function_id_to_exc_handlers[function_id];
if (!source->count) {
return SUCCESS_RESULT ();
}
if (!source->handlers) {
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "Invalid exception handler table");
}
HBCExceptionHandler *handlers = (HBCExceptionHandler *)calloc (source->count, sizeof (HBCExceptionHandler));
if (!handlers) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Cannot allocate exception handlers");
}
for (u32 i = 0; i < source->count; i++) {
handlers[i].start = source->handlers[i].start;
handlers[i].end = source->handlers[i].end;
handlers[i].target = source->handlers[i].target;
}
out->handlers = handlers;
out->count = source->count;
return SUCCESS_RESULT ();
}
void hbc_free_exception_handlers(HBCExceptionHandlerArray *arr) {
if (arr) {
R_FREE (arr->handlers);
arr->count = 0;
}
}
Result hbc_get_function_frame_size(HBC *hbc, u32 function_id, u32 *out) {
if (!hbc || !out || !hbc->reader.function_headers || function_id >= hbc->reader.header.functionCount) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
}
*out = hbc->reader.function_headers[function_id].frameSize;
return SUCCESS_RESULT ();
}
@@ -491,7 +622,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.data || !hbc->reader.function_headers) {
if (!hbc->reader.file_buffer || !hbc->reader.function_headers) {
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "Bytecode data is not loaded");
}
if (function_id >= hbc->reader.header.functionCount) {
@@ -504,10 +635,30 @@ 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);
}
if (fh->offset > hbc->reader.file_buffer.size || size > hbc->reader.file_buffer.size - fh->offset) {
ut64 file_size = r_buf_size (hbc->reader.file_buffer);
if (fh->offset > file_size || size > file_size - fh->offset) {
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "Function bytecode range is out of bounds");
}
*out_ptr = hbc->reader.file_buffer.data + fh->offset;
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_size = size;
return SUCCESS_RESULT ();
}
@@ -525,19 +676,18 @@ Result hbc_decomp_fn(
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
}
StringBuffer sb;
Result res = _hbc_sb_init (&sb, 16 * 1024);
RStrBuf sb;
r_strbuf_init (&sb);
Result res = _hbc_decompile_function_with_state (hbc, function_id, options, &sb);
if (res.code != RESULT_SUCCESS) {
r_strbuf_fini (&sb);
return res;
}
res = _hbc_decompile_function_with_state (hbc, function_id, options, &sb);
if (res.code != RESULT_SUCCESS) {
_hbc_sb_free (&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");
}
*out_str = sb.data;
return SUCCESS_RESULT ();
}
@@ -549,19 +699,18 @@ Result hbc_decomp_all(
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments");
}
StringBuffer sb;
Result res = _hbc_sb_init (&sb, 32 * 1024);
RStrBuf sb;
r_strbuf_init (&sb);
Result res = _hbc_decompile_all_with_state (hbc, options, &sb);
if (res.code != RESULT_SUCCESS) {
r_strbuf_fini (&sb);
return res;
}
res = _hbc_decompile_all_with_state (hbc, options, &sb);
if (res.code != RESULT_SUCCESS) {
_hbc_sb_free (&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");
}
*out_str = sb.data;
return SUCCESS_RESULT ();
}
@@ -660,7 +809,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);
@@ -686,10 +835,8 @@ 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"));
StringBuffer sb;
if (_hbc_sb_init (&sb, 256).code != RESULT_SUCCESS) {
return NULL;
}
RStrBuf sb;
r_strbuf_init (&sb);
Result fmt_res;
if (is_obj) {
const u32 key_count = ovs[1];
@@ -703,23 +850,21 @@ 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;
if (fmt_res.code == RESULT_SUCCESS && sb.length > 0) {
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) {
size_t base_len = strlen (base_text);
result = (char *)malloc (base_len + 4 + sb.length + 1);
result = (char *)malloc (base_len + 4 + (size_t)sb_len + 1);
if (result) {
memcpy (result, base_text, base_len);
memcpy (result + base_len, " ; ", 4);
memcpy (result + base_len + 4, sb.data, sb.length + 1);
memcpy (result + base_len + 4, sb_text, (size_t)sb_len + 1);
}
}
_hbc_sb_free (&sb);
r_strbuf_fini (&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");
+13 -21
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef HBC_INTERNAL_H
#define HBC_INTERNAL_H
@@ -6,32 +6,22 @@
#include <hbc/hbc.h>
#include <hbc/parser.h>
#include <hbc/literals.h>
#include <r_vec.h>
#include <stdlib.h>
#include <string.h>
/* 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 ();
static inline void hbc_literal_entry_fini(HBCLiteralEntry *entry) {
if (!entry) {
return;
}
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 ();
free (entry->formatted);
free (entry->xref_addrs);
}
R_VEC_TYPE_WITH_FINI(RVecHBCLiteralEntry, HBCLiteralEntry, hbc_literal_entry_fini)
typedef struct {
HBCLiteralEntry *entries;
u32 count;
u32 cap;
RVecHBCLiteralEntry entries;
} HBCLiteralCache;
struct HBC {
@@ -41,6 +31,8 @@ struct HBC {
u32 string_count;
const char **strings;
u32 function_count;
u32 *function_flags;
bool function_flags_ready;
HBCLiteralCache lit_cache;
};
+36 -71
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* Public literals API: lazy cache, formatting, scanning. See
* include/hbc/literals.h for the contract. */
@@ -12,6 +12,12 @@
#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);
@@ -72,11 +78,8 @@ 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;
StringBuffer sb;
Result ir = _hbc_sb_init (&sb, 128);
if (ir.code != RESULT_SUCCESS) {
return ir;
}
RStrBuf sb;
r_strbuf_init (&sb);
Result fr;
if (kind == HBC_LIT_ARRAY) {
fr = _hbc_format_array_literal (r, num_items, primary_id, &sb, LITERALS_PRETTY_NEVER, true);
@@ -85,13 +88,14 @@ 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);
}
if (fr.code == RESULT_SUCCESS && sb.data && sb.length > 0) {
*out = strdup (sb.data);
const char *text = r_strbuf_get (&sb);
if (fr.code == RESULT_SUCCESS && text && r_strbuf_length (&sb) > 0) {
*out = strdup (text);
if (!*out) {
fr = ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "oom");
}
}
_hbc_sb_free (&sb);
r_strbuf_fini (&sb);
return fr;
}
@@ -121,8 +125,7 @@ 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 (u32 i = 0; i < c->count; i++) {
HBCLiteralEntry *e = &c->entries[i];
for (HBCLiteralEntry *e = R_VEC_START_ITER (&c->entries); e != R_VEC_END_ITER (&c->entries); e++) {
if (e->kind == kind && e->num_items == num_items && e->primary_id == primary_id && e->secondary_id == secondary_id) {
return e;
}
@@ -130,18 +133,8 @@ static HBCLiteralEntry *cache_find(HBCLiteralCache *c, HBCLiteralKind kind, u32
return NULL;
}
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) {
/* Anchor objects at their values, not their shared keys/shape. */
static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id, u32 secondary_id) {
const HBCReader *r = &hbc->reader;
if (kind == HBC_LIT_ARRAY) {
if (!r->arrays) {
@@ -152,18 +145,10 @@ static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id) {
}
return r->arrays_paddr + primary_id;
}
/* object */
if (r->header.version >= 97 && r->object_shapes && primary_id < r->object_shape_count) {
u32 key_off = r->object_shapes[primary_id].key_buffer_offset;
if (r->object_keys && key_off < r->header.objKeyBufferSize) {
return r->object_keys_paddr + key_off;
}
if (!r->object_values || secondary_id >= r->header.objValueBufferSize) {
return 0;
}
if (!r->object_keys || primary_id >= r->header.objKeyBufferSize) {
return 0;
}
return r->object_keys_paddr + primary_id;
return r->object_values_paddr + secondary_id;
}
static Result cache_get_or_create(HBC *hbc, HBCLiteralKind kind, u32 num_items, u32 primary_id, u32 secondary_id, bool format, HBCLiteralEntry **out_entry) {
@@ -179,15 +164,16 @@ static Result cache_get_or_create(HBC *hbc, HBCLiteralKind kind, u32 num_items,
*out_entry = e;
return SUCCESS_RESULT ();
}
e = cache_alloc (&hbc->lit_cache);
e = RVecHBCLiteralEntry_emplace_back (&hbc->lit_cache.entries);
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);
e->paddr = entry_paddr (hbc, kind, primary_id, secondary_id);
if (format) {
char *txt = NULL;
Result fr = format_raw_impl (&hbc->reader, kind, num_items, primary_id, secondary_id, &txt);
@@ -204,10 +190,7 @@ Result hbc_literals_get(HBC *hbc, HBCLiteralKind kind, u32 num_items, u32 primar
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "invalid args");
}
HBCLiteralEntry *e;
Result r = cache_get_or_create (hbc, kind, num_items, primary_id, secondary_id, true, &e);
if (r.code != RESULT_SUCCESS) {
return r;
}
RETURN_IF_ERROR (cache_get_or_create (hbc, kind, num_items, primary_id, secondary_id, true, &e));
*out_formatted = e->formatted;
return SUCCESS_RESULT ();
}
@@ -232,10 +215,7 @@ static Result register_literal(HBC *hbc, HBCLiteralKind kind, u32 num_items, u32
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "invalid args");
}
HBCLiteralEntry *e;
Result r = cache_get_or_create (hbc, kind, num_items, primary_id, secondary_id, format, &e);
if (r.code != RESULT_SUCCESS) {
return r;
}
RETURN_IF_ERROR (cache_get_or_create (hbc, kind, num_items, primary_id, secondary_id, format, &e));
if (from_addr) {
xref_add (e, from_addr);
}
@@ -250,13 +230,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 = hbc->lit_cache.entries;
*out_count = hbc->lit_cache.count;
*out = R_VEC_START_ITER (&hbc->lit_cache.entries);
*out_count = (u32)RVecHBCLiteralEntry_length (&hbc->lit_cache.entries);
return SUCCESS_RESULT ();
}
u32 hbc_literals_count(HBC *hbc) {
return hbc? hbc->lit_cache.count: 0;
return hbc? (u32)RVecHBCLiteralEntry_length (&hbc->lit_cache.entries): 0;
}
void hbc_literals_reset(HBC *hbc) {
@@ -264,14 +244,8 @@ void hbc_literals_reset(HBC *hbc) {
return;
}
HBCLiteralCache *c = &hbc->lit_cache;
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;
RVecHBCLiteralEntry_fini (&c->entries);
RVecHBCLiteralEntry_init (&c->entries);
}
/* ---------- Code-side scan ---------- */
@@ -488,7 +462,7 @@ static Result scan_code(HBC *hbc, bool filter_kind, HBCLiteralKind want_kind, bo
}
}
if (out_count) {
*out_count = hbc->lit_cache.count;
*out_count = (u32)RVecHBCLiteralEntry_length (&hbc->lit_cache.entries);
}
return SUCCESS_RESULT ();
}
@@ -582,12 +556,8 @@ Result hbc_literals_scan_pool(HBC *hbc, HBCLiteralKind kind, HBCPoolGroup **out,
return SUCCESS_RESULT ();
}
u32 cap = 64;
HBCPoolGroup *arr = calloc (cap, sizeof (HBCPoolGroup));
if (!arr) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "oom");
}
u32 n = 0;
RVecHBCPoolGroup groups;
RVecHBCPoolGroup_init (&groups);
size_t pos = 0;
while (pos < size) {
size_t group_start = pos;
@@ -596,16 +566,11 @@ Result hbc_literals_scan_pool(HBC *hbc, HBCLiteralKind kind, HBCPoolGroup **out,
if (!slp_skip_group (base, size, &pos, &tag, &length)) {
break;
}
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++;
HBCPoolGroup group = hbc_pool_group (paddr + (u32)group_start, (u32)group_start, length, tag);
RVecHBCPoolGroup_push_back (&groups, &group);
}
*out = arr;
*out_count = n;
*out = R_VEC_START_ITER (&groups);
*out_count = (u32)RVecHBCPoolGroup_length (&groups);
memset (&groups, 0, sizeof (groups));
return SUCCESS_RESULT ();
}
+103 -112
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <hbc/disasm.h>
#include <stdio.h>
@@ -18,13 +18,14 @@ Result _hbc_disassembler_init(Disassembler *disassembler, HBCReader *reader, HBC
}
disassembler->current_function_id = 0;
return _hbc_sb_init (&disassembler->output, 8192);
r_strbuf_init (&disassembler->output);
return SUCCESS_RESULT ();
}
/* Clean up disassembler */
void _hbc_disassembler_cleanup(Disassembler *disassembler) {
if (disassembler) {
_hbc_sb_free (&disassembler->output);
r_strbuf_fini (&disassembler->output);
}
}
@@ -35,7 +36,7 @@ Result _hbc_print_function_header(Disassembler *disassembler, FunctionHeader *fu
}
HBCReader *reader = disassembler->reader;
StringBuffer *output = &disassembler->output;
RStrBuf *output = &disassembler->output;
bool verbose = disassembler->options.verbose;
/* Get function name with validation */
@@ -48,16 +49,19 @@ Result _hbc_print_function_header(Disassembler *disassembler, FunctionHeader *fu
}
if (verbose) {
RETURN_IF_ERROR (_hbc_sb_appendf (output,
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_appendf (output,
RETURN_IF_ERROR (r_strbuf_appendf (output,
", read index sz=%u, write index sz=%u, strict=%s, exc handler=%s, debug info=%s",
function_header->highestReadCacheIndex,
function_header->highestWriteCacheIndex,
@@ -67,35 +71,38 @@ Result _hbc_print_function_header(Disassembler *disassembler, FunctionHeader *fu
}
/* Offset information */
RETURN_IF_ERROR (_hbc_sb_appendf (output, " @ offset 0x%08x", function_header->offset));
RETURN_IF_ERROR (r_strbuf_appendf (output, " @ offset 0x%08x", function_header->offset));
/* Exception handler information */
if (function_header->hasExceptionHandler && disassembler->options.show_debug_info) {
RETURN_IF_ERROR (_hbc_sb_append (output, "\n [Exception handlers:"));
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_appendf (output,
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (output, " ]"));
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_appendf (output,
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (output, "\n\n"));
RETURN_IF_ERROR (r_strbuf_append (output, "\n\n"));
return SUCCESS_RESULT ();
}
@@ -103,7 +110,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, StringBuffer *out) {
static Result format_operand_asm(Disassembler *d, ParsedInstruction *ins, int idx, RStrBuf *out) {
OperandType t = ins->inst->operands[idx].operand_type;
OperandMeaning m = ins->inst->operands[idx].operand_meaning;
u32 v = hbc_operand_value (ins, idx);
@@ -112,15 +119,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_sb_appendf (out, "r%u", v);
return HBC_TO_RESULT (r_strbuf_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_sb_appendf (out, "0x%x", off);
return HBC_TO_RESULT (r_strbuf_appendf (out, "0x%x", off));
}
return _hbc_sb_appendf (out, "%u", v);
return HBC_TO_RESULT (r_strbuf_appendf (out, "%u", v));
}
if (m == OPERAND_MEANING_STRING_ID) {
@@ -140,44 +147,44 @@ static Result format_operand_asm(Disassembler *d, ParsedInstruction *ins, int id
}
}
u32 abs = r->string_storage_file_offset + off;
return _hbc_sb_appendf (out, "0x%x", abs);
return HBC_TO_RESULT (r_strbuf_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_sb_appendf (out, "0x%x", file_abs);
return HBC_TO_RESULT (r_strbuf_appendf (out, "0x%x", file_abs));
}
if (t == OPERAND_TYPE_DOUBLE) {
double double_val = ins->double_arg2;
return _hbc_sb_appendf (out, "%.6f", double_val);
return HBC_TO_RESULT (r_strbuf_appendf (out, "%.6f", double_val));
}
/* Default: decimal immediate */
return _hbc_sb_appendf (out, "%u", v);
return HBC_TO_RESULT (r_strbuf_appendf (out, "%u", v));
}
static Result print_instruction_asm(Disassembler *disassembler, ParsedInstruction *instruction) {
StringBuffer *out = &disassembler->output;
RStrBuf *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 (_hbc_sb_appendf (out, "0x%08x: ", fh->offset + instruction->original_pos));
RETURN_IF_ERROR (r_strbuf_appendf (out, "0x%08x: ", fh->offset + instruction->original_pos));
/* mnemonic — tables are already snake_case */
RETURN_IF_ERROR (_hbc_sb_append (out, instruction->inst->name));
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (out, first? " ": ", "));
RETURN_IF_ERROR (r_strbuf_append (out, first? " ": ", "));
first = false;
RETURN_IF_ERROR (format_operand_asm (disassembler, instruction, i, out));
}
RETURN_IF_ERROR (_hbc_sb_append (out, "\n"));
RETURN_IF_ERROR (r_strbuf_append (out, "\n"));
return SUCCESS_RESULT ();
}
@@ -190,14 +197,14 @@ Result _hbc_print_instruction(Disassembler *disassembler, ParsedInstruction *ins
if (disassembler->options.asm_syntax) {
return print_instruction_asm (disassembler, instruction);
}
StringBuffer *output = &disassembler->output;
RStrBuf *output = &disassembler->output;
HBCReader *reader = disassembler->reader;
FunctionHeader *fh = &reader->function_headers[disassembler->current_function_id];
/* Print instruction address */
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, ">: <"));
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, ">: <"));
/* Print operands */
bool first = true;
@@ -208,29 +215,29 @@ Result _hbc_print_instruction(Disassembler *disassembler, ParsedInstruction *ins
}
if (!first) {
RETURN_IF_ERROR (_hbc_sb_append (output, ", "));
RETURN_IF_ERROR (r_strbuf_append (output, ", "));
}
first = false;
/* Print operand name and value */
const char *operand_name = hbc_operand_name (&instruction->inst->operands[i]);
RETURN_IF_ERROR (_hbc_sb_append (output, operand_name));
RETURN_IF_ERROR (_hbc_sb_append (output, ": "));
RETURN_IF_ERROR (r_strbuf_append (output, operand_name));
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append_int (output, value));
RETURN_IF_ERROR (r_strbuf_appendf (output, "%u", value));
} else {
double double_val = instruction->double_arg2;
RETURN_IF_ERROR (_hbc_sb_appendf (output, "%.6f", double_val));
RETURN_IF_ERROR (r_strbuf_appendf (output, "%.6f", double_val));
}
}
RETURN_IF_ERROR (_hbc_sb_append (output, ">"));
RETURN_IF_ERROR (r_strbuf_append (output, ">"));
/* Add comments for special operands */
@@ -246,22 +253,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 (_hbc_sb_append (output, " # String: \""));
RETURN_IF_ERROR (_hbc_sb_append (output, reader->strings[value]));
RETURN_IF_ERROR (_hbc_sb_append (output, "\" ("));
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, "\" ("));
if (value < reader->header.stringCount && reader->string_kinds) {
RETURN_IF_ERROR (_hbc_sb_append (output, _hbc_string_kind_to_string (reader->string_kinds[value])));
RETURN_IF_ERROR (r_strbuf_append (output, _hbc_string_kind_to_string (reader->string_kinds[value])));
} else {
RETURN_IF_ERROR (_hbc_sb_append (output, "Unknown"));
RETURN_IF_ERROR (r_strbuf_append (output, "Unknown"));
}
RETURN_IF_ERROR (_hbc_sb_append (output, ")"));
RETURN_IF_ERROR (r_strbuf_append (output, ")"));
}
break;
case OPERAND_MEANING_BIGINT_ID:
if (value < reader->bigint_count) {
RETURN_IF_ERROR (_hbc_sb_append (output, " # BigInt: "));
RETURN_IF_ERROR (_hbc_sb_append_int (output, reader->bigint_values[value]));
RETURN_IF_ERROR (r_strbuf_append (output, " # BigInt: "));
RETURN_IF_ERROR (r_strbuf_appendf (output, "%lld", (long long)reader->bigint_values[value]));
}
break;
@@ -274,10 +281,13 @@ Result _hbc_print_instruction(Disassembler *disassembler, ParsedInstruction *ins
func_name = reader->strings[func->functionName];
}
RETURN_IF_ERROR (_hbc_sb_appendf (output,
RETURN_IF_ERROR (r_strbuf_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;
@@ -299,28 +309,28 @@ Result _hbc_print_instruction(Disassembler *disassembler, ParsedInstruction *ins
u32 value = hbc_operand_value (instruction, i);
RETURN_IF_ERROR (_hbc_sb_appendf (output, " # Address: %08x", fh->offset + instruction->original_pos + value));
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (output, " # Jump table: ["));
RETURN_IF_ERROR (r_strbuf_append (output, " # Jump table: ["));
for (u32 i = 0; i < instruction->switch_jump_table_size; i++) {
if (i > 0) {
RETURN_IF_ERROR (_hbc_sb_append (output, ", "));
RETURN_IF_ERROR (r_strbuf_append (output, ", "));
}
RETURN_IF_ERROR (_hbc_sb_appendf (output, "%08x", fh->offset + instruction->switch_jump_table[i]));
RETURN_IF_ERROR (r_strbuf_appendf (output, "%08x", fh->offset + instruction->switch_jump_table[i]));
}
RETURN_IF_ERROR (_hbc_sb_append (output, "]"));
RETURN_IF_ERROR (r_strbuf_append (output, "]"));
}
}
/* End the instruction */
RETURN_IF_ERROR (_hbc_sb_append (output, "\n"));
RETURN_IF_ERROR (r_strbuf_append (output, "\n"));
return SUCCESS_RESULT ();
}
@@ -344,9 +354,9 @@ Result _hbc_disassemble_function(Disassembler *disassembler, u32 function_id) {
/* Print bytecode listing header */
if (disassembler->options.asm_syntax) {
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "Bytecode listing (asm):\n\n"));
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "Bytecode listing (asm):\n\n"));
} else {
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "Bytecode listing:\n\n"));
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "Bytecode listing:\n\n"));
}
/* Debug mode - always show function offset info */
@@ -362,71 +372,57 @@ 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 (_hbc_sb_append (&disassembler->output,
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (&disassembler->output,
RETURN_IF_ERROR (r_strbuf_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 >= reader->file_buffer.size) {
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output,
if (function_header->offset >= r_buf_size (reader->file_buffer)) {
RETURN_IF_ERROR (r_strbuf_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 > reader->file_buffer.size - function_header->offset) {
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output,
if (function_header->bytecodeSizeInBytes > r_buf_size (reader->file_buffer) - function_header->offset) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output,
"[Bytecode extends beyond file size, truncating]\n"));
function_header->bytecodeSizeInBytes = reader->file_buffer.size - function_header->offset;
function_header->bytecodeSizeInBytes = r_buf_size (reader->file_buffer) - function_header->offset;
}
/* Allocate bytecode buffer */
function_header->bytecode = (u8 *)malloc (function_header->bytecodeSizeInBytes);
if (!function_header->bytecode) {
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "[Memory allocation failed for bytecode]\n"));
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "[Memory allocation failed for bytecode]\n"));
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate bytecode buffer");
}
/* Save current position */
size_t saved_pos = reader->file_buffer.position;
/* 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 seek_result;
}
/* Read bytecode data */
Result read_result = _hbc_buffer_reader_read_bytes (&reader->file_buffer,
function_header->bytecode,
function_header->bytecodeSizeInBytes);
bool read_ok = (u32)r_buf_read_at (reader->file_buffer,
function_header->offset,
function_header->bytecode,
function_header->bytecodeSizeInBytes) == 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"));
if (!read_ok) {
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "[Failed to read bytecode data]\n"));
free (function_header->bytecode);
function_header->bytecode = NULL;
return read_result;
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Failed to read bytecode data");
}
/* 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 (_hbc_sb_append (&disassembler->output,
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output,
"[Warning: First byte doesn't look like a valid opcode]\n"));
}
}
@@ -439,26 +435,28 @@ Result _hbc_disassemble_function(Disassembler *disassembler, u32 function_id) {
if (result.code != RESULT_SUCCESS) {
/* Handle parsing error - Print more debug info */
RETURN_IF_ERROR (_hbc_sb_appendf (&disassembler->output,
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (&disassembler->output, "Raw bytecode: "));
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "Raw bytecode: "));
for (u32 i = 0; i < function_header->bytecodeSizeInBytes; i++) {
RETURN_IF_ERROR (_hbc_sb_appendf (&disassembler->output, "%02x ", function_header->bytecode[i]));
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (&disassembler->output, "\n "));
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "\n "));
}
}
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "\n\n"));
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "\n\n"));
}
/* Print instructions */
@@ -476,8 +474,8 @@ Result _hbc_disassemble_function(Disassembler *disassembler, u32 function_id) {
}
/* End the function disassembly */
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "\n\n"));
RETURN_IF_ERROR (_hbc_sb_append (&disassembler->output, "===============\n\n"));
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "\n\n"));
RETURN_IF_ERROR (r_strbuf_append (&disassembler->output, "===============\n\n"));
return SUCCESS_RESULT ();
}
@@ -527,7 +525,7 @@ Result _hbc_output_disassembly(Disassembler *disassembler, const char *output_fi
}
/* Write the output */
fputs (disassembler->output.data, out);
fputs (R_STRBUF_SAFEGET (&disassembler->output), out);
/* Close the file if we opened it */
if (output_file) {
@@ -546,13 +544,10 @@ Result _hbc_disassemble_file(const char *input_file, const char *output_file, HB
/* Initialize HBC reader */
HBCReader reader;
Result result = _hbc_reader_init (&reader);
if (result.code != RESULT_SUCCESS) {
return result;
}
RETURN_IF_ERROR (_hbc_reader_init (&reader));
/* Read the whole file */
result = _hbc_reader_read_whole_file (&reader, input_file);
Result result = _hbc_reader_read_whole_file (&reader, input_file);
if (result.code != RESULT_SUCCESS) {
_hbc_reader_cleanup (&reader);
return result;
@@ -592,20 +587,16 @@ Result _hbc_disassemble_buffer(const u8 *buffer, size_t size, const char *output
/* Initialize HBC reader */
HBCReader reader;
Result result = _hbc_reader_init (&reader);
if (result.code != RESULT_SUCCESS) {
return result;
}
RETURN_IF_ERROR (_hbc_reader_init (&reader));
/* Initialize buffer reader */
result = _hbc_buffer_reader_init_from_memory (&reader.file_buffer, buffer, size);
if (result.code != RESULT_SUCCESS) {
reader.file_buffer = r_buf_new_with_bytes (buffer, size);
if (!reader.file_buffer) {
_hbc_reader_cleanup (&reader);
return result;
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate buffer");
}
/* Read header */
result = _hbc_reader_read_header (&reader);
Result result = _hbc_reader_read_header (&reader);
if (result.code != RESULT_SUCCESS) {
_hbc_reader_cleanup (&reader);
return result;
+1 -1
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <hbc/hermes_encoder.h>
#include <hbc/opcodes.h>
+4 -1
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
@@ -32,6 +32,7 @@
#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]))
@@ -64,6 +65,7 @@ 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 {
@@ -99,6 +101,7 @@ static const IsaVersion k_isa_versions[] = {
{ 95, &k_isa_v95 },
{ 96, &k_isa_v96 },
{ 97, &k_isa_v97 },
{ 98, &k_isa_v98 },
{ 99, &k_isa_v99 },
};
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+3 -3
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+10 -10
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[77] = { "call_direct", { { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[78] = { "call2", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[79] = { "call3", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 } }, 6 },
[80] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[81] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[82] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[86] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
@@ -98,10 +98,10 @@ static const Instruction k_instructions_v76[256] = {
[91] = { "async_break_check", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[92] = { "profile_point", { { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[93] = { "unreachable", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[94] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[95] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[96] = { "create_generator_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[97] = { "create_generator_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[94] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[95] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[96] = { "create_generator_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[97] = { "create_generator_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[98] = { "create_this", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[99] = { "select_object", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[100] = { "load_param", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
@@ -129,8 +129,8 @@ static const Instruction k_instructions_v76[256] = {
[122] = { "start_generator", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[123] = { "resume_generator", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[124] = { "complete_generator", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[125] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[126] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[125] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[126] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[127] = { "iterator_begin", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[128] = { "iterator_next", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[129] = { "iterator_close", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[88] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+4 -4
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[87] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[88] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[83] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[87] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[88] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[89] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[86] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[84] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[86] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[89] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[90] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+14 -14
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[78] = { "call_direct", { { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[79] = { "call2", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[80] = { "call3", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 } }, 6 },
[81] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[82] = { "call_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[83] = { "construct_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[86] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[84] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[85] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[86] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[89] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[90] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
@@ -100,12 +100,12 @@ static const Instruction k_instructions_v84[256] = {
[93] = { "debugger", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[94] = { "async_break_check", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[95] = { "profile_point", { { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[96] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[97] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[98] = { "create_generator_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[99] = { "create_generator_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[100] = { "create_async_closure", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[101] = { "create_async_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[96] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[97] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[98] = { "create_generator_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[99] = { "create_generator_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[100] = { "create_async_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[101] = { "create_async_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[102] = { "create_this", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[103] = { "select_object", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[104] = { "load_param", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
@@ -134,8 +134,8 @@ static const Instruction k_instructions_v84[256] = {
[127] = { "start_generator", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[128] = { "resume_generator", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[129] = { "complete_generator", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[130] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[131] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[130] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[131] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[132] = { "iterator_begin", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[133] = { "iterator_next", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[134] = { "iterator_close", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[92] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[93] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[94] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[86] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[87] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[88] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[90] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[91] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[92] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[92] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[93] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[94] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[92] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[93] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[94] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
+5 -5
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[88] = { "call_direct_long_index", { { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[89] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[90] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[91] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[92] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[93] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[94] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
+4 -4
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[102] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[103] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[101] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[102] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[103] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[104] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[105] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[106] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
+227
View File
@@ -0,0 +1,227 @@
/* r2hermes - BSD - Copyright 2026 - Ahmeth4n */
#define SHORT_OPERAND_TYPES
#include <hbc/opcodes.h>
/* Generated from Hermes bytecode version 98 (BytecodeList.def) */
static const Instruction k_instructions_v98[219] = {
[0] = { "unreachable", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[1] = { "new_object_with_buffer", { { R8, 0 }, { U16, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[2] = { "new_object_with_buffer_long", { { R8, 0 }, { U32, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 10 },
[3] = { "new_object_with_buffer_and_parent", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 } }, 11 },
[4] = { "new_object", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[5] = { "new_object_with_parent", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[6] = { "new_array_with_buffer", { { R8, 0 }, { U16, 0 }, { U16, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 } }, 8 },
[7] = { "new_array_with_buffer_long", { { R8, 0 }, { U16, 0 }, { U16, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 } }, 10 },
[8] = { "new_array", { { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[9] = { "new_fast_array", { { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[10] = { "fast_array_length", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[11] = { "fast_array_load", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[12] = { "fast_array_store", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[13] = { "fast_array_push", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[14] = { "fast_array_append", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[15] = { "cache_new_object", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 8 },
[16] = { "mov", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[17] = { "mov_long", { { R32, 0 }, { R32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 9 },
[18] = { "negate", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[19] = { "not", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[20] = { "bit_not", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[21] = { "type_of", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[22] = { "eq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[23] = { "strict_eq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[24] = { "neq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[25] = { "strict_neq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[26] = { "less", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[27] = { "less_eq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[28] = { "greater", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[29] = { "greater_eq", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[30] = { "add", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[31] = { "add_n", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[32] = { "add_s", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[33] = { "mul", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[34] = { "mul_n", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[35] = { "div", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[36] = { "div_n", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[37] = { "mod", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[38] = { "sub", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[39] = { "sub_n", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[40] = { "l_shift", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[41] = { "r_shift", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[42] = { "u_rshift", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[43] = { "bit_and", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[44] = { "bit_xor", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[45] = { "bit_or", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[46] = { "inc", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[47] = { "dec", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[48] = { "instance_of", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[49] = { "is_in", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[50] = { "private_is_in", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[51] = { "type_of_is", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[52] = { "get_parent_environment", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[53] = { "get_environment", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[54] = { "get_closure_environment", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[55] = { "store_to_environment", { { R8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[56] = { "store_to_environment_l", { { R8, 0 }, { U16, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[57] = { "store_np_to_environment", { { R8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[58] = { "store_np_to_environment_l", { { R8, 0 }, { U16, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[59] = { "load_from_environment", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[60] = { "load_from_environment_l", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[61] = { "get_global_object", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[62] = { "get_new_target", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[63] = { "load_parent_no_traps", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[64] = { "create_function_environment", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[65] = { "create_top_level_environment", { { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[66] = { "create_environment", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[67] = { "declare_global_var", { { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[68] = { "get_by_id_short", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U8, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 5 },
[69] = { "get_by_id", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[70] = { "get_by_id_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[71] = { "get_by_id_with_receiver_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 } }, 9 },
[72] = { "try_get_by_id", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[73] = { "try_get_by_id_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[74] = { "put_by_id_loose", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[75] = { "put_by_id_strict", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[76] = { "put_by_id_loose_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[77] = { "put_by_id_strict_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[78] = { "try_put_by_id_loose", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[79] = { "try_put_by_id_strict", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 6 },
[80] = { "try_put_by_id_loose_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[81] = { "try_put_by_id_strict_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[82] = { "put_own_by_slot_idx", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[83] = { "put_own_by_slot_idx_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[84] = { "get_own_by_slot_idx", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[85] = { "get_own_by_slot_idx_long", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[86] = { "define_own_by_id", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[87] = { "define_own_by_id_long", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 } }, 8 },
[88] = { "define_own_by_index", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[89] = { "define_own_by_index_l", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[90] = { "define_own_in_dense_array", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[91] = { "define_own_in_dense_array_l", { { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[92] = { "define_own_by_val", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[93] = { "get_by_val", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[94] = { "get_by_index", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[95] = { "get_by_val_with_receiver", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[96] = { "put_by_val_loose", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[97] = { "put_by_val_strict", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[98] = { "put_by_val_with_receiver", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 } }, 6 },
[99] = { "del_by_val", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[100] = { "add_own_private_by_sym", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[101] = { "get_own_private_by_sym", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[102] = { "put_own_private_by_sym", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[103] = { "define_own_getter_setter_by_val", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 } }, 6 },
[104] = { "get_p_name_list", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[105] = { "get_next_p_name", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 } }, 6 },
[106] = { "call", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[107] = { "construct", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[108] = { "call1", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[109] = { "call_with_new_target", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[110] = { "call2", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[111] = { "call3", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 } }, 6 },
[112] = { "call4", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 } }, 7 },
[113] = { "call_with_new_target_long", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[114] = { "call_require", { { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[115] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[116] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[117] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[118] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[119] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[120] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[121] = { "throw", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[122] = { "throw_if_empty", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[123] = { "throw_if_undefined", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[124] = { "throw_if_this_initialized", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[125] = { "debugger", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[126] = { "async_break_check", { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 1 },
[127] = { "profile_point", { { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[128] = { "create_base_class", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[129] = { "create_base_class_long_index", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 } }, 8 },
[130] = { "create_derived_class", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 } }, 7 },
[131] = { "create_derived_class_long_index", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { R8, 0 }, { U32, 0 }, { 0, 0 } }, 9 },
[132] = { "create_closure", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[133] = { "create_closure_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[134] = { "create_this_for_new", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[135] = { "create_this_for_super", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[136] = { "select_object", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[137] = { "load_param", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[138] = { "load_param_long", { { R8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[139] = { "load_const_u_int8", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[140] = { "load_const_int", { { R8, 0 }, { I32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[141] = { "load_const_double", { { R8, 0 }, { DBL, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 10 },
[142] = { "load_const_big_int", { { R8, 0 }, { U16, OPERAND_MEANING_BIGINT_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[143] = { "load_const_big_int_long_index", { { R8, 0 }, { U32, OPERAND_MEANING_BIGINT_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[144] = { "load_const_string", { { R8, 0 }, { U16, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[145] = { "load_const_string_long_index", { { R8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[146] = { "load_const_empty", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[147] = { "load_const_undefined", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[148] = { "load_const_null", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[149] = { "load_const_true", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[150] = { "load_const_false", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[151] = { "load_const_zero", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[152] = { "coerce_this_ns", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[153] = { "load_this_ns", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[154] = { "to_number", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[155] = { "to_numeric", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[156] = { "to_int32", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[157] = { "add_empty_string", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[158] = { "create_private_name", { { R8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[159] = { "get_arguments_prop_by_val_loose", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[160] = { "get_arguments_prop_by_val_strict", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[161] = { "get_arguments_length", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[162] = { "reify_arguments_strict", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[163] = { "reify_arguments_loose", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[164] = { "to_property_key", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[165] = { "create_reg_exp", { { R8, 0 }, { U32, OPERAND_MEANING_STRING_ID }, { U32, OPERAND_MEANING_STRING_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 } }, 14 },
[166] = { "u_int_switch_imm", { { R8, 0 }, { U32, 0 }, { A32, 0 }, { U32, 0 }, { U32, 0 }, { 0, 0 } }, 18 },
[167] = { "string_switch_imm", { { R8, 0 }, { U32, 0 }, { U32, 0 }, { A32, 0 }, { U32, 0 }, { 0, 0 } }, 18 },
[168] = { "create_generator", { { R8, 0 }, { R8, 0 }, { U16, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[169] = { "create_generator_long_index", { { R8, 0 }, { R8, 0 }, { U32, OPERAND_MEANING_FUNCTION_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[170] = { "iterator_begin", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[171] = { "iterator_next", { { R8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[172] = { "iterator_close", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[173] = { "typed_load_parent", { { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[174] = { "jmp", { { A8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[175] = { "jmp_long", { { A32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 5 },
[176] = { "jmp_true", { { A8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[177] = { "jmp_true_long", { { A32, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[178] = { "jmp_false", { { A8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[179] = { "jmp_false_long", { { A32, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[180] = { "jmp_undefined", { { A8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[181] = { "jmp_undefined_long", { { A32, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 6 },
[182] = { "jmp_type_of_is", { { A32, 0 }, { R8, 0 }, { U16, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 8 },
[183] = { "j_less", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[184] = { "j_less_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[185] = { "j_not_less", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[186] = { "j_not_less_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[187] = { "j_less_n", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[188] = { "j_less_n_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[189] = { "j_not_less_n", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[190] = { "j_not_less_n_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[191] = { "j_less_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[192] = { "j_less_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[193] = { "j_not_less_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[194] = { "j_not_less_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[195] = { "j_less_equal_n", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[196] = { "j_less_equal_n_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[197] = { "j_not_less_equal_n", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[198] = { "j_not_less_equal_n_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[199] = { "j_greater", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[200] = { "j_greater_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[201] = { "j_not_greater", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[202] = { "j_not_greater_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[203] = { "j_greater_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[204] = { "j_greater_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[205] = { "j_not_greater_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[206] = { "j_not_greater_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[207] = { "j_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[208] = { "j_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[209] = { "j_not_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[210] = { "j_not_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[211] = { "j_strict_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[212] = { "j_strict_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[213] = { "j_strict_not_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[214] = { "j_strict_not_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[215] = { "jmp_builtin_is", { { A8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[216] = { "jmp_builtin_is_long", { { A32, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[217] = { "jmp_builtin_is_not", { { A8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[218] = { "jmp_builtin_is_not_long", { { A32, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
};
+8 -8
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[116] = { "call_builtin_long", { { R8, 0 }, { U8, 0 }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[117] = { "get_builtin_closure", { { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[115] = { "call_builtin", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[116] = { "call_builtin_long", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { U32, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[117] = { "get_builtin_closure", { { R8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 3 },
[118] = { "ret", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[119] = { "catch", { { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 2 },
[120] = { "direct_eval", { { R8, 0 }, { R8, 0 }, { U8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
@@ -223,8 +223,8 @@ static const Instruction k_instructions_v99[223] = {
[216] = { "j_strict_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[217] = { "j_strict_not_equal", { { A8, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[218] = { "j_strict_not_equal_long", { { A32, 0 }, { R8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[219] = { "jmp_builtin_is", { { A8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[220] = { "jmp_builtin_is_long", { { A32, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[221] = { "jmp_builtin_is_not", { { A8, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[222] = { "jmp_builtin_is_not_long", { { A32, 0 }, { U8, 0 }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[219] = { "jmp_builtin_is", { { A8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[220] = { "jmp_builtin_is_long", { { A32, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
[221] = { "jmp_builtin_is_not", { { A8, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 4 },
[222] = { "jmp_builtin_is_not_long", { { A32, 0 }, { U8, OPERAND_MEANING_BUILTIN_ID }, { R8, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, 7 },
};
+73 -123
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <hbc/bytecode.h>
#include <hbc/opcodes.h>
@@ -120,29 +120,25 @@ 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 (bytecode_buffer.position < bytecode_buffer.size) {
while (r_buf_tell (bytecode_buffer) < r_buf_size (bytecode_buffer)) {
/* Remember the original position */
size_t original_pos = bytecode_buffer.position;
size_t original_pos = r_buf_tell (bytecode_buffer);
/* Make sure we have at least one byte to read */
if (bytecode_buffer.position >= bytecode_buffer.size) {
if (r_buf_tell (bytecode_buffer) >= r_buf_size (bytecode_buffer)) {
break;
}
/* Read opcode */
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;
}
u8 opcode = r_buf_read8 (bytecode_buffer);
/* Find instruction definition */
const Instruction *inst = NULL;
@@ -153,7 +149,7 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
}
}
if (!inst) {
size_t remaining = bytecode_buffer.size > original_pos? (bytecode_buffer.size - original_pos): 0;
size_t remaining = r_buf_size (bytecode_buffer) > original_pos? (r_buf_size (bytecode_buffer) - original_pos): 0;
if (remaining == 0) {
break;
}
@@ -174,10 +170,15 @@ 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;
RETURN_IF_ERROR (_hbc_parsed_instruction_list_add (out_instructions, &instruction));
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;
}
/* Stop parsing this function and move on. */
bytecode_buffer.position = bytecode_buffer.size;
r_buf_seek (bytecode_buffer, r_buf_size (bytecode_buffer), R_BUF_SET);
break;
}
@@ -217,7 +218,9 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
case OPERAND_TYPE_DOUBLE: need = 8; break;
default: need = 0; break;
}
if (need && (bytecode_buffer.position > bytecode_buffer.size || need > bytecode_buffer.size - bytecode_buffer.position)) {
ut64 pos = r_buf_tell (bytecode_buffer);
ut64 size = r_buf_size (bytecode_buffer);
if (need && (pos > size || need > size - pos)) {
parsing_failed = true;
break;
}
@@ -225,24 +228,11 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
switch (operand_type) {
case OPERAND_TYPE_REG8:
case OPERAND_TYPE_UINT8:
{
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;
}
operand_values[i] = r_buf_read8 (bytecode_buffer);
break;
case OPERAND_TYPE_ADDR8:
{
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;
}
u8 value_u = r_buf_read8 (bytecode_buffer);
/* Sign-extend relative offsets */
i8 value_s = (i8)value_u;
operand_values[i] = (u32) (i32)value_s;
@@ -250,38 +240,17 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
}
case OPERAND_TYPE_UINT16:
{
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;
}
operand_values[i] = r_buf_read_le16 (bytecode_buffer);
break;
case OPERAND_TYPE_REG32:
case OPERAND_TYPE_UINT32:
case OPERAND_TYPE_IMM32:
{
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;
}
operand_values[i] = r_buf_read_le32 (bytecode_buffer);
break;
case OPERAND_TYPE_ADDR32:
{
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;
}
u32 value_u = r_buf_read_le32 (bytecode_buffer);
/* Sign-extend relative offsets */
i32 value_s = (i32)value_u;
operand_values[i] = (u32)value_s;
@@ -290,11 +259,8 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
case OPERAND_TYPE_DOUBLE:
{
double value;
Result read_result = _hbc_buffer_reader_read_u64 (&bytecode_buffer, (u64 *)&value);
if (read_result.code != RESULT_SUCCESS) {
parsing_failed = true;
break;
}
u64 raw = r_buf_read_le64 (bytecode_buffer);
memcpy (&value, &raw, sizeof (value));
has_double_arg2 = true;
double_arg2_value = value;
break;
@@ -354,13 +320,14 @@ 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 = reader->file_buffer.size;
size_t fsz = r_buf_size (reader->file_buffer);
u32 candidates[3];
candidates[0] = function_header->offset + (u32)original_pos + base; /* instruction-relative */
candidates[1] = function_header->offset + base; /* function-relative */
@@ -374,8 +341,6 @@ 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++) {
@@ -384,26 +349,17 @@ Result _hbc_parse_function_bytecode(HBCReader *reader, u32 function_id, ParsedIn
continue;
}
/* Seek and align to 4 bytes */
Result seek_result = _hbc_buffer_reader_seek (&reader->file_buffer, jt_off);
if (seek_result.code != RESULT_SUCCESS) {
ut64 table_pos = R_ROUND (jt_off, 4);
if (table_pos > fsz) {
continue;
}
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) {
ut64 table_bytes = (ut64)jump_table_size * sizeof (u32);
if (table_bytes > fsz - table_pos) {
continue;
}
ut64 table_end = table_pos + table_bytes;
/* Jump tables live after the bytecode body, before the next function. */
if (! (reader->file_buffer.position >= func_start && table_end <= table_limit)) {
if (! (table_pos >= func_start && table_end <= table_limit)) {
continue;
}
@@ -413,24 +369,13 @@ 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;
Result rr = _hbc_buffer_reader_read_u32 (&reader->file_buffer, &rel);
if (rr.code != RESULT_SUCCESS) {
fail = true;
break;
}
u32 rel = r_buf_read_le32_at (reader->file_buffer, table_pos + (ut64)i * sizeof (u32));
instruction.switch_jump_table[i] = (u32)original_pos + rel;
}
if (!fail) {
read_ok = true;
}
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++) {
@@ -443,10 +388,12 @@ 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 ();
}
@@ -483,17 +430,17 @@ u32 _hbc_compute_target_address(const ParsedInstruction *insn, int op_index) {
}
/* Convert instruction to string */
Result _hbc_instruction_to_string(ParsedInstruction *instruction, StringBuffer *out_string) {
Result _hbc_instruction_to_string(ParsedInstruction *instruction, RStrBuf *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 (_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, ">: <"));
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, ">: <"));
/* Get operands */
bool first = true;
@@ -503,7 +450,7 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, StringBuffer *
}
if (!first) {
RETURN_IF_ERROR (_hbc_sb_append (out_string, ", "));
RETURN_IF_ERROR (r_strbuf_append (out_string, ", "));
}
first = false;
@@ -514,15 +461,15 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, StringBuffer *
const char *operand_name = hbc_operand_name (&instruction->inst->operands[i]);
/* Print operand name and value */
RETURN_IF_ERROR (_hbc_sb_append (out_string, operand_name));
RETURN_IF_ERROR (_hbc_sb_append (out_string, ": "));
RETURN_IF_ERROR (r_strbuf_append (out_string, operand_name));
RETURN_IF_ERROR (r_strbuf_append (out_string, ": "));
/* Format value based on operand type */
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, "%u", value));
RETURN_IF_ERROR (r_strbuf_appendf (out_string, "%u", value));
}
/* Close operands bracket */
RETURN_IF_ERROR (_hbc_sb_append (out_string, ">"));
RETURN_IF_ERROR (r_strbuf_append (out_string, ">"));
/* Add comments for special operands */
HBCReader *reader = instruction->hbc_reader;
@@ -538,18 +485,18 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, StringBuffer *
switch (operand_meaning) {
case OPERAND_MEANING_STRING_ID:
if (value < reader->header.stringCount) {
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, ")"));
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, ")"));
}
break;
case OPERAND_MEANING_BIGINT_ID:
if (value < reader->bigint_count) {
RETURN_IF_ERROR (_hbc_sb_append (out_string, " # BigInt: "));
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, "%lld", (long long)reader->bigint_values[value]));
RETURN_IF_ERROR (r_strbuf_append (out_string, " # BigInt: "));
RETURN_IF_ERROR (r_strbuf_appendf (out_string, "%lld", (long long)reader->bigint_values[value]));
}
break;
@@ -561,17 +508,20 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, StringBuffer *
func_name = reader->strings[func->functionName];
}
RETURN_IF_ERROR (_hbc_sb_appendf (out_string,
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (out_string, " # Built-in function: "));
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, "#%u", value));
RETURN_IF_ERROR (r_strbuf_append (out_string, " # Built-in function: "));
RETURN_IF_ERROR (r_strbuf_appendf (out_string, "#%u", value));
break;
default:
@@ -591,24 +541,24 @@ Result _hbc_instruction_to_string(ParsedInstruction *instruction, StringBuffer *
/* For addresses, calculate absolute target address */
u32 absolute_address = instruction->function_offset + instruction->original_pos + value;
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, " # Address: %08x", absolute_address));
RETURN_IF_ERROR (r_strbuf_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 (_hbc_sb_append (out_string, " # Jump table: ["));
RETURN_IF_ERROR (r_strbuf_append (out_string, " # Jump table: ["));
for (u32 i = 0; i < instruction->switch_jump_table_size; i++) {
if (i > 0) {
RETURN_IF_ERROR (_hbc_sb_append (out_string, ", "));
RETURN_IF_ERROR (r_strbuf_append (out_string, ", "));
}
RETURN_IF_ERROR (_hbc_sb_appendf (out_string, "%08x", instruction->function_offset + instruction->switch_jump_table[i]));
RETURN_IF_ERROR (r_strbuf_appendf (out_string, "%08x", instruction->function_offset + instruction->switch_jump_table[i]));
}
RETURN_IF_ERROR (_hbc_sb_append (out_string, "]"));
RETURN_IF_ERROR (r_strbuf_append (out_string, "]"));
}
}
File diff suppressed because it is too large. Load diff
+61 -391
View File
@@ -1,7 +1,6 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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) {
@@ -13,432 +12,103 @@ 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");
}
/* Initialize HBC reader */
HBCReader reader;
Result result = _hbc_reader_init (&reader);
if (result.code != RESULT_SUCCESS) {
return result;
}
/* Read and parse the file */
result = _hbc_reader_read_file (&reader, input_file);
HBC *hbc = NULL;
Result result = hbc_open (input_file, &hbc);
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");
}
}
/* Generate r2 script header */
HBCHeader header = { 0 };
result = hbc_get_header (hbc, &header);
if (result.code != RESULT_SUCCESS) {
if (output_file) {
fclose (out);
}
hbc_close (hbc);
return result;
}
fprintf (out, "# R2 script for Hermes bytecode file: %s\n", input_file);
fprintf (out, "# Generated by libhbctool\n\n");
/* 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, "# 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);
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));
/* ================= 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 */
const u32 function_count = R_MIN (hbc_function_count (hbc), 50000);
fprintf (out, "\n# Function flags\n");
for (u32 i = 0; i < function_count; i++) {
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 */
HBCFunc func = { 0 };
if (hbc_get_function_info (hbc, i, &func).code != RESULT_SUCCESS) {
continue;
}
/* 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]);
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);
} else {
fprintf (out, "'f func.hermes.%s=0x%x\n", sanitized_name, function_offsets[i]);
fprintf (out, "'f func.hermes.%s=0x%x\n", name, func.offset);
}
}
fprintf (out, "# Successfully read %u function headers\n\n", function_count);
/* 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);
}
}
}
}
}
}
}
}
}
}
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;
}
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);
}
/* Clean up */
_hbc_reader_cleanup (&reader);
hbc_close (hbc);
return SUCCESS_RESULT ();
}
-172
View File
@@ -1,172 +0,0 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
#include <hbc/common.h>
Result _hbc_buffer_reader_init_from_file(BufferReader *reader, const char *filename) {
if (!reader || !filename) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Reader or filename is NULL");
}
FILE *file = fopen (filename, "rb");
if (!file) {
return ERROR_RESULT (RESULT_ERROR_FILE_NOT_FOUND, "Failed to open file");
}
/* Get file size */
fseek (file, 0, SEEK_END);
long file_size = ftell (file);
fseek (file, 0, SEEK_SET);
if (file_size <= 0) {
fclose (file);
return ERROR_RESULT (RESULT_ERROR_INVALID_FORMAT, "Empty or invalid file");
}
/* Allocate buffer */
reader->data = (u8 *)malloc (file_size);
if (!reader->data) {
fclose (file);
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate buffer for file");
}
/* Read file contents */
size_t bytes_read = fread (reader->data, 1, file_size, file);
fclose (file);
if (bytes_read != (size_t)file_size) {
free (reader->data);
reader->data = NULL;
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Failed to read entire file");
}
reader->size = file_size;
reader->position = 0;
return SUCCESS_RESULT ();
}
Result _hbc_buffer_reader_init_from_memory(BufferReader *reader, const u8 *data, size_t size) {
if (!reader || !data || size == 0) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for _hbc_buffer_reader_init_from_memory");
}
/* Copy the data to ensure ownership */
reader->data = (u8 *)malloc (size);
if (!reader->data) {
return ERROR_RESULT (RESULT_ERROR_MEMORY_ALLOCATION, "Failed to allocate buffer");
}
memcpy (reader->data, data, size);
reader->size = size;
reader->position = 0;
return SUCCESS_RESULT ();
}
/* Shared validity + bounds check for a read of `need` bytes at the cursor. */
static Result buffer_reader_check(const BufferReader *reader, size_t need) {
if (!reader || !reader->data) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid buffer reader");
}
if (reader->position > reader->size || need > reader->size - reader->position) {
hbc_debug_printf ("Warning: Buffer overflow prevented reading %zu bytes at position %zu of %zu\n",
need,
reader->position,
reader->size);
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Buffer overflow");
}
return SUCCESS_RESULT ();
}
/* Read `n` (1..8) little-endian bytes at the cursor, advancing the position.
* *out is zeroed on any failure. */
static Result buffer_reader_read_le(BufferReader *reader, size_t n, u64 *out) {
*out = 0;
RETURN_IF_ERROR (buffer_reader_check (reader, n));
u64 v = 0;
for (size_t i = 0; i < n; i++) {
v |= (u64)reader->data[reader->position + i] << (8 * i);
}
reader->position += n;
*out = v;
return SUCCESS_RESULT ();
}
/* Define the fixed-width little-endian readers; all share buffer_reader_read_le. */
#define HBC_DEFINE_READ(bits) \
Result _hbc_buffer_reader_read_u ## bits (BufferReader *reader, u ## bits *out_value) { \
if (!out_value) { \
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "read out_value is NULL"); \
} \
u64 v; \
Result r = buffer_reader_read_le (reader, sizeof (u ## bits), &v); \
*out_value = (u ## bits)v; \
return r; \
}
HBC_DEFINE_READ(8)
HBC_DEFINE_READ(16)
HBC_DEFINE_READ(32)
HBC_DEFINE_READ(64)
#undef HBC_DEFINE_READ
Result _hbc_buffer_reader_read_bytes(BufferReader *reader, u8 *out_buffer, size_t length) {
if (!out_buffer) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Invalid arguments for _hbc_buffer_reader_read_bytes");
}
RETURN_IF_ERROR (buffer_reader_check (reader, length));
memcpy (out_buffer, reader->data + reader->position, length);
reader->position += length;
return SUCCESS_RESULT ();
}
Result _hbc_buffer_reader_seek(BufferReader *reader, size_t position) {
if (!reader) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Reader is NULL");
}
/* Safety check - is the reader data valid? */
if (!reader->data) {
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "BufferReader has no data");
}
/* Validate position */
if (position > reader->size) {
hbc_debug_printf ("Warning: Attempted to seek beyond buffer bounds (pos: %zu, size: %zu)\n",
position,
reader->size);
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Seek position beyond buffer size");
}
reader->position = position;
return SUCCESS_RESULT ();
}
Result _hbc_buffer_reader_align(BufferReader *reader, size_t alignment) {
if (!reader) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Reader is NULL");
}
if (alignment == 0) {
return ERROR_RESULT (RESULT_ERROR_INVALID_ARGUMENT, "Alignment must be non-zero");
}
size_t remainder = reader->position % alignment;
if (remainder != 0) {
size_t padding = alignment - remainder;
if (reader->position > reader->size || padding > reader->size - reader->position) {
return ERROR_RESULT (RESULT_ERROR_PARSING_FAILED, "Buffer overflow in align");
}
reader->position += padding;
}
return SUCCESS_RESULT ();
}
void _hbc_buffer_reader_free(BufferReader *reader) {
if (reader && reader->data) {
free (reader->data);
reader->data = NULL;
reader->size = 0;
reader->position = 0;
}
}
-188
View File
@@ -1,188 +0,0 @@
/* 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';
}
+7 -4
View File
@@ -6,15 +6,18 @@ 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
CFLAGS += $(shell pkg-config --cflags r_core 2>/dev/null || echo "-I/usr/local/include/libr")
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)")
R2_PLUGDIR?=$(shell r2 -H R2_USER_PLUGINS)
R2_PREFIX?=$(shell r2 -H R2_PREFIX)
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")
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)")
# 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
@@ -33,7 +36,7 @@ BIN_SRC = bin_hbc.c
ASM_SRC = asm_hbc.c
CORE_SRC = core_hbc.c
CORE_ONE_SRC = core_hbc_one.c
HBC_HEADERS = $(shell find ../../include -name '*.h') utils.inc.c sbom.inc.c
HBC_HEADERS = $(shell find ../../include -name '*.h') utils.inc.c sbom.inc.c version.inc.c
# Object files
ARCH_OBJ = arch_hbc.o
@@ -206,7 +209,7 @@ help:
# Dependencies
$(HERMES_LIB):
@echo "Building libhbc library..."
$(MAKE) -C ../.. all
$(MAKE) -C ../.. build/libhbc.a
# Default target
.DEFAULT_GOAL := all
+324 -44
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <r_anal.h>
#include <hbc/hbc.h>
@@ -13,9 +13,17 @@
#define R_ARCH_SYNTAX_CAMEL 6
#endif
typedef struct {
u32 offset;
u32 size;
u32 frame_size;
} HermesFunctionFrame;
typedef struct {
ut32 bytecode_version; /* cached effective HBC bytecode version */
HBC *hbc; /* Hermes state for direct access */
HermesFunctionFrame *function_frames;
u32 function_frame_count;
u32 string_count;
u32 overflow_string_count;
const void *small_string_table;
@@ -23,6 +31,12 @@ typedef struct {
u64 string_storage_offset;
} HermesArchSession;
static int function_frame_cmp(const void *a, const void *b) {
const HermesFunctionFrame *fa = (const HermesFunctionFrame *)a;
const HermesFunctionFrame *fb = (const HermesFunctionFrame *)b;
return fa->offset < fb->offset? -1: fa->offset > fb->offset;
}
static ut32 parse_cpu_version(const char *cpu) {
if (!cpu || !*cpu) {
return 0;
@@ -87,6 +101,24 @@ static bool load_string_tables(HermesArchSession *hs, RArchSession *s) {
}
hs->bytecode_version = hh.version;
const u32 function_count = hbc_function_count (hs->hbc);
hs->function_frames = R_NEWS0 (HermesFunctionFrame, function_count);
if (function_count && !hs->function_frames) {
return false;
}
for (u32 i = 0; i < function_count; i++) {
HBCFunc fi = { 0 };
u32 frame_size = 0;
if (hbc_get_function_info (hs->hbc, i, &fi).code == RESULT_SUCCESS && fi.size &&
hbc_get_function_frame_size (hs->hbc, i, &frame_size).code == RESULT_SUCCESS) {
HermesFunctionFrame *frame = &hs->function_frames[hs->function_frame_count++];
frame->offset = fi.offset;
frame->size = fi.size;
frame->frame_size = frame_size;
}
}
qsort (hs->function_frames, hs->function_frame_count, sizeof (HermesFunctionFrame), function_frame_cmp);
/* Get string count */
hs->string_count = hbc_string_count (hs->hbc);
@@ -127,7 +159,94 @@ static bool opcode_is_conditional(u8 opcode) {
return false;
}
static void parse_operands_and_set_ptr(RAnalOp *op, const ut8 *bytes, ut32 size, ut8 opcode, HermesArchSession *hs) {
/* Return the register-frame size for the function containing addr. The cached
* intervals are sorted by bytecode offset, so this stays logarithmic even for
* large React Native bundles with thousands of functions. */
static bool function_frame_size(HermesArchSession *hs, ut64 addr, ut32 *frame_size) {
if (!hs || !hs->function_frames || !frame_size) {
return false;
}
const ut64 file_addr = addr >= HBC_VADDR_BASE? addr - HBC_VADDR_BASE: addr;
u32 lo = 0;
u32 hi = hs->function_frame_count;
while (lo < hi) {
const u32 mid = lo + (hi - lo) / 2;
if ((ut64)hs->function_frames[mid].offset <= file_addr) {
lo = mid + 1;
} else {
hi = mid;
}
}
if (!lo) {
return false;
}
/* Multiple deduplicated function-table entries may share the body. */
const u32 candidate_offset = hs->function_frames[lo - 1].offset;
u32 candidate_frame_size = 0;
for (u32 i = lo; i > 0; i--) {
const HermesFunctionFrame *frame = &hs->function_frames[i - 1];
if (frame->offset != candidate_offset) {
break;
}
if (file_addr < (ut64)frame->offset + frame->size) {
candidate_frame_size = R_MAX (candidate_frame_size, frame->frame_size);
}
}
*frame_size = candidate_frame_size;
return candidate_frame_size > 0;
}
static bool implicit_call_arg_count(ut8 opcode, const ut32 values[6], ut32 *argc) {
switch (opcode) {
case OP_Call:
case OP_CallLong:
case OP_Construct:
case OP_ConstructLong:
*argc = values[2];
return true;
case OP_CallDirect:
case OP_CallDirectLongIndex:
*argc = values[1];
return true;
case OP_CallBuiltin:
case OP_CallBuiltinLong:
*argc = values[2];
return true;
default:
return false;
}
}
/* Append the implicit call-frame window after the encoded operands. Hermes
* counts `this` in argc and stores it plus source arguments immediately below
* the six reserved frame-header registers. */
static void append_call_args(PJ *pj, HermesArchSession *hs, ut64 addr, ut8 opcode, const ut32 values[6]) {
ut32 argc = 0;
ut32 frame_size = 0;
if (!pj || !implicit_call_arg_count (opcode, values, &argc) || !argc ||
!function_frame_size (hs, addr, &frame_size)) {
return;
}
const st64 base = (st64)frame_size - 1 - HBC_CALL_FRAME_RESERVED;
if (base < 0 || argc > (ut32) (base + 1)) {
return;
}
for (ut32 i = 0; i < argc; i++) {
char reg[16];
snprintf (reg, sizeof (reg), "r%" PFMT64d, base - i);
pj_o (pj);
pj_ks (pj, "type", "reg");
pj_ks (pj, "value", reg);
pj_kb (pj, "implicit", true);
pj_ks (pj, "kind", i? "call_arg": "call_this");
if (i) {
pj_kn (pj, "index", i - 1);
}
pj_end (pj);
}
}
static void parse_operands(RAnalOp *op, const ut8 *bytes, ut32 size, ut8 opcode, ut8 canonical_opcode, HermesArchSession *hs, RArchDecodeMask mask) {
HBCISA isa = hbc_isa_getv (hs->bytecode_version);
const Instruction *inst_set = isa.instructions;
if (!inst_set || opcode >= isa.count) {
@@ -135,91 +254,211 @@ static void parse_operands_and_set_ptr(RAnalOp *op, const ut8 *bytes, ut32 size,
}
const Instruction *inst = &inst_set[opcode];
PJ *pj = NULL;
if (mask & R_ARCH_OP_MASK_OPEX) {
pj = pj_new ();
if (pj) {
pj_o (pj);
pj_ka (pj, "operands");
}
}
// Parse operands
ut32 operand_values[6] = { 0 };
size_t pos = 1; // Skip opcode byte
size_t pos = 1; /* Skip opcode byte */
ut32 values[6] = { 0 };
for (int i = 0; i < 6 && inst->operands[i].operand_type != OPERAND_TYPE_NONE; i++) {
if (pos >= size) {
break;
}
const InstructionOperand *operand = &inst->operands[i];
ut32 value = 0;
double double_value = 0.0;
bool valid = false;
switch (inst->operands[i].operand_type) {
case OPERAND_TYPE_REG8:
case OPERAND_TYPE_UINT8:
case OPERAND_TYPE_ADDR8:
if (pos < size) {
operand_values[i] = bytes[pos];
if (pos + 1 <= size) {
value = bytes[pos];
pos += 1;
valid = true;
}
break;
case OPERAND_TYPE_UINT16:
if (pos + 1 < size) {
operand_values[i] = (bytes[pos + 1] << 8) | bytes[pos];
if (pos + 2 <= size) {
value = r_read_le16 (bytes + pos);
pos += 2;
valid = true;
}
break;
case OPERAND_TYPE_REG32:
case OPERAND_TYPE_UINT32:
case OPERAND_TYPE_ADDR32:
case OPERAND_TYPE_IMM32:
if (pos + 3 < size) {
operand_values[i] = (bytes[pos + 3] << 24) | (bytes[pos + 2] << 16) |
(bytes[pos + 1] << 8) | bytes[pos];
if (pos + 4 <= size) {
value = r_read_le32 (bytes + pos);
pos += 4;
valid = true;
}
break;
case OPERAND_TYPE_DOUBLE:
/* Double is 8 bytes - just advance position, value not used for ptr setting */
if (pos + 7 < size) {
if (pos + 8 <= size) {
ut64 bits = r_read_le64 (bytes + pos);
memcpy (&double_value, &bits, sizeof (double_value));
pos += 8;
valid = true;
}
break;
case OPERAND_TYPE_NONE:
default:
/* OPERAND_TYPE_NONE is never reached due to loop condition */
break;
}
if (!valid) {
break;
}
values[i] = value;
// Check if this operand is a string ID
if (inst->operands[i].operand_meaning == OPERAND_MEANING_STRING_ID) {
ut32 string_id = operand_values[i];
ut64 ref_addr = UT64_MAX;
ut64 string_byte_length = UT64_MAX;
/* Resolve IDs without replacing their raw values in OPEX. */
if (operand->operand_meaning == OPERAND_MEANING_STRING_ID) {
ut32 string_id = value;
if (string_id < hs->string_count && hs->hbc) {
HBCStringMeta meta;
Result meta_result = hbc_get_string_meta (hs->hbc, string_id, &meta);
if (meta_result.code == RESULT_SUCCESS) {
op->ptr = (st64) (HBC_VADDR_BASE + meta.offset);
ref_addr = HBC_VADDR_BASE + meta.offset;
op->ptr = (st64)ref_addr;
string_byte_length = meta.isUTF16? (ut64)meta.length * 2: meta.length;
/* Expose the string byte length (size of the pointed
* contents) so the disassembler caps r2's string read and
* picks the right flag among overlapping, non-null-terminated
* Hermes strings that share this storage offset. */
op->ptrsize = meta.isUTF16? (int) (meta.length * 2): (int)meta.length;
op->ptrsize = (int)string_byte_length;
}
}
}
// Check if this operand is a function ID
else if (inst->operands[i].operand_meaning == OPERAND_MEANING_FUNCTION_ID) {
ut32 function_id = operand_values[i];
} else if (operand->operand_meaning == OPERAND_MEANING_FUNCTION_ID) {
ut32 function_id = value;
if (hs->hbc) {
ut32 offset = 0;
HBCFunc fi;
Result func_result = hbc_get_function_info (hs->hbc, function_id, &fi);
if (func_result.code == RESULT_SUCCESS) {
// name = fi.name;
offset = fi.offset;
(void)fi.size;
(void)fi.param_count;
op->ptr = (st64) (HBC_VADDR_BASE + offset);
ref_addr = HBC_VADDR_BASE + fi.offset;
op->ptr = (st64)ref_addr;
}
}
}
if (pj) {
pj_o (pj);
if (operand->operand_type == OPERAND_TYPE_REG8 || operand->operand_type == OPERAND_TYPE_REG32) {
char reg[16];
snprintf (reg, sizeof (reg), "r%u", value);
pj_ks (pj, "type", "reg");
pj_ks (pj, "value", reg);
} else {
pj_ks (pj, "type", "imm");
if (operand->operand_type == OPERAND_TYPE_DOUBLE) {
pj_kd (pj, "value", double_value);
} else if (operand->operand_meaning != OPERAND_MEANING_NONE) {
pj_kn (pj, "value", value);
} else if (operand->operand_type == OPERAND_TYPE_ADDR8) {
pj_kN (pj, "value", (st8)value);
} else if (operand->operand_type == OPERAND_TYPE_ADDR32 || operand->operand_type == OPERAND_TYPE_IMM32) {
pj_kN (pj, "value", (st32)value);
} else {
pj_kn (pj, "value", value);
}
}
if (operand->operand_meaning != OPERAND_MEANING_NONE) {
pj_ks (pj, "kind", hbc_operand_name (operand));
}
if (ref_addr != UT64_MAX) {
pj_kn (pj, "address", ref_addr);
}
if (string_byte_length != UT64_MAX) {
/* Unlike RAnalOp.ptrsize, OPEX can represent a known empty
* string without conflating its zero length with "unset". */
pj_kn (pj, "byte_length", string_byte_length);
}
pj_end (pj);
}
}
if (pj) {
append_call_args (pj, hs, op->addr, canonical_opcode, values);
pj_end (pj); /* operands */
pj_end (pj);
r_strbuf_set (&op->opex, pj_string (pj));
pj_free (pj);
}
}
/* SwitchImm stores instruction-relative deltas in a 4-byte aligned table
* outside the instruction stream. Publish the decoded cases through the
* standard switch_op interface so r2 analysis and external decompilers receive
* the same control-flow facts as libhbc's built-in decompiler. */
static bool decode_switch_op(RArchSession *s, RAnalOp *op) {
if (!s || !s->arch || !op || !op->bytes || op->size < 18) {
return false;
}
const ut32 table_delta = r_read_le32 (op->bytes + 2);
const st32 default_delta = (st32)r_read_le32 (op->bytes + 6);
const st32 min_val = (st32)r_read_le32 (op->bytes + 10);
const st32 max_val = (st32)r_read_le32 (op->bytes + 14);
const st64 amount64 = (st64)max_val - min_val + 1;
if (amount64 <= 0 || amount64 > UT16_MAX) {
return false;
}
const ut32 amount = (ut32)amount64;
const ut64 unaligned_table = op->addr + table_delta;
if (unaligned_table < op->addr) {
return false;
}
const ut64 table_addr = R_ROUND (unaligned_table, 4);
const ut64 default_target = op->addr + default_delta;
RBin *bin = s->arch->binb.bin;
if (!bin || !bin->iob.read_at) {
return false;
}
const int table_size = (int)amount * 4;
ut8 *table = malloc (table_size);
if (!table) {
return false;
}
if (!bin->iob.read_at (bin->iob.io, table_addr, table, table_size)) {
free (table);
return false;
}
op->switch_op = r_anal_switch_op_new (op->addr, (ut64) (st64)min_val, (ut64) (st64)max_val, default_target);
if (!op->switch_op) {
free (table);
return false;
}
#if R2_ABIVERSION >= 33
op->switch_op->daddr = table_addr;
op->switch_op->dsize = 4;
op->switch_op->amount = amount;
#endif
for (ut32 i = 0; i < amount; i++) {
const st32 delta = (st32)r_read_le32 (table + i * 4);
const ut64 target = op->addr + delta;
if (target != default_target) {
r_anal_switch_op_add_case (op->switch_op, table_addr + i * 4, (ut64) (st64)min_val + i, target);
}
}
free (table);
op->fail = default_target;
op->eob = true;
return true;
}
static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
R_RETURN_VAL_IF_FAIL (s && op, false);
(void)mask;
if (!op->bytes) {
return false;
@@ -272,18 +511,19 @@ static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
/* asm.syntax=camel → convert snake_case mnemonic to CamelCase */
if (camel && sinfo.text) {
char camel_buf[128];
hbc_snake_to_camel (sinfo.text, camel_buf, sizeof (camel_buf));
free (sinfo.text);
sinfo.text = strdup (camel_buf);
char *camel_text = r_str_snake_to_camel (sinfo.text);
if (camel_text) {
free (sinfo.text);
sinfo.text = camel_text;
}
}
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 operands and set ptr for string/function references */
parse_operands_and_set_ptr (op, op->bytes, op->size, sinfo.opcode, hs);
/* Parse structured operands and set ptr for string/function references. */
parse_operands (op, op->bytes, op->size, sinfo.opcode, sinfo.canonical_opcode, hs, mask);
if (mask & R_ARCH_OP_MASK_ESIL && sinfo.text) {
set_esil (op, op->bytes, op->addr);
@@ -509,6 +749,7 @@ static bool decode(RArchSession *s, RAnalOp *op, RArchDecodeMask mask) {
/* Switch operations */
case OP_SwitchImm:
op->type = R_ANAL_OP_TYPE_SWITCH;
decode_switch_op (s, op);
break;
/* Special operations */
case OP_Unreachable:
@@ -641,6 +882,8 @@ static bool fini(RArchSession *s) {
}
HermesArchSession *hs = (HermesArchSession *)s->data;
if (hs) {
R_FREE (hs->function_frames);
hs->function_frame_count = 0;
if (hs->hbc) {
hbc_close (hs->hbc);
hs->hbc = NULL;
@@ -651,6 +894,48 @@ static bool fini(RArchSession *s) {
return true;
}
/* LoadParam reads a Hermes call-frame slot, not an architectural register.
* Keep the slot index as an ESIL operand and use a custom value-producing op
* so register-access analysis never invents arg0/arg1/... register names.
* Concrete emulation cannot recover the caller-owned frame value here; zero is
* the conventional unknown placeholder, while OPEX retains the exact slot. */
static bool esil_hbc_load_param(REsil *esil) {
R_RETURN_VAL_IF_FAIL (esil, false);
#if R2_ABIVERSION >= 87
const RStrs slot = r_esil_pop (esil);
if (r_strs_empty (slot)) {
return false;
}
#else
char *slot = r_esil_pop (esil);
if (!slot) {
return false;
}
free (slot);
#endif
return r_esil_pushnum (esil, 0);
}
#if R2_ABIVERSION >= 109
static bool esilcb(RArchSession *s R_UNUSED, REsil *esil, RArchEsilAction action) {
#else
static bool esilcb(RArchSession *s, RArchEsilAction action) {
REsil *esil = s && s->arch? s->arch->esil: NULL;
#endif
if (!esil) {
return false;
}
switch (action) {
case R_ARCH_ESIL_ACTION_INIT:
return r_esil_set_op (esil, "HBC_LOAD_PARAM", esil_hbc_load_param,
1, 1, R_ESIL_OP_TYPE_CUSTOM, NULL);
case R_ARCH_ESIL_ACTION_FINI:
return true;
default:
return false;
}
}
/* Register profile for ESIL emulation */
static char *regs(RArchSession *s) {
(void)s;
@@ -659,14 +944,8 @@ static char *regs(RArchSession *s) {
return NULL;
}
/* Define special registers */
/* Hermes frame arguments and VM registers have no fixed ABI homes. */
r_strbuf_append (sb, "=PC\tpc\n");
r_strbuf_append (sb, "=SP\tr0\n");
r_strbuf_append (sb, "=BP\tr1\n");
r_strbuf_append (sb, "=A0\tr2\n");
r_strbuf_append (sb, "=A1\tr3\n");
r_strbuf_append (sb, "=A2\tr4\n");
r_strbuf_append (sb, "=A3\tr5\n");
/* Program counter - 64 bits at offset 0 */
r_strbuf_append (sb, "gpr\tpc\t.64\t0\t0\n");
@@ -696,6 +975,7 @@ const RArchPlugin r_arch_plugin_r2hermes = {
.regs = regs,
.info = archinfo,
.mnemonics = mnemonics,
.esilcb = esilcb,
.init = init,
.fini = fini,
};
+1 -1
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <r_asm.h>
+239 -17
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <r_bin.h>
#include <r_bin_dwarf.h>
@@ -11,6 +11,10 @@
typedef struct {
HBC *hbc;
#if R2_ABIVERSION >= 142
RVecRBinTrycatch trycatch;
bool trycatch_loaded;
#endif
} HBCBinObj;
static bool check(RBinFile *bf, RBuffer *b) {
@@ -28,13 +32,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);
if (bo) {
bo->hbc = hbc;
bf->bo->bin_obj = bo;
bf->buf = buf;
return true;
}
hbc_safe_close (&hbc);
bo->hbc = hbc;
#if R2_ABIVERSION >= 142
RVecRBinTrycatch_init (&bo->trycatch);
#endif
bf->bo->bin_obj = bo;
bf->buf = buf;
return true;
}
}
return false;
@@ -43,6 +47,9 @@ static bool load(RBinFile *bf, RBuffer *buf, ut64 R_UNUSED loadaddr) {
static void destroy(RBinFile *bf) {
HBCBinObj *bo = bf->bo->bin_obj;
if (bo) {
#if R2_ABIVERSION >= 142
RVecRBinTrycatch_fini (&bo->trycatch);
#endif
hbc_safe_close (&bo->hbc);
free (bo);
}
@@ -187,9 +194,6 @@ 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;
@@ -245,10 +249,110 @@ static RList *entries(RBinFile *bf) {
return entries;
}
#if R2_ABIVERSION >= 142
static RVecRBinTrycatch *trycatch(RBinFile *bf) {
HBCBinObj *bo = R_UNWRAP3 (bf, bo, bin_obj);
if (!bo || bo->trycatch_loaded) {
return bo? &bo->trycatch: NULL;
}
HBC *hbc = bo->hbc;
if (!hbc) {
return NULL;
}
const u32 function_count = hbc_function_count (hbc);
for (u32 function_id = 0; function_id < function_count; function_id++) {
HBCFunc function;
if (hbc_get_function_info (hbc, function_id, &function).code != RESULT_SUCCESS) {
continue;
}
HBCExceptionHandlerArray handlers = { 0 };
if (hbc_get_function_exception_handlers (hbc, function_id, &handlers).code != RESULT_SUCCESS) {
continue;
}
const ut64 source = HBC_VADDR_BASE + function.offset;
for (u32 i = 0; i < handlers.count; i++) {
const HBCExceptionHandler *handler = &handlers.handlers[i];
if (handler->start >= handler->end || handler->end > function.size || handler->target >= function.size) {
continue;
}
(void)r_bin_trycatch_add (&bo->trycatch,
source,
source + handler->start,
source + handler->end,
source + handler->target,
0);
}
hbc_free_exception_handlers (&handlers);
}
bo->trycatch_loaded = true;
return &bo->trycatch;
}
#elif R2_ABIVERSION >= 14
static RList *trycatch(RBinFile *bf) {
RList *ret = r_list_newf ((RListFree)r_bin_trycatch_free);
HBC *hbc = get_hbc (bf);
if (!ret || !hbc) {
return ret;
}
const u32 function_count = hbc_function_count (hbc);
for (u32 function_id = 0; function_id < function_count; function_id++) {
HBCFunc function;
if (hbc_get_function_info (hbc, function_id, &function).code != RESULT_SUCCESS) {
continue;
}
HBCExceptionHandlerArray handlers = { 0 };
if (hbc_get_function_exception_handlers (hbc, function_id, &handlers).code != RESULT_SUCCESS) {
continue;
}
const ut64 source = HBC_VADDR_BASE + function.offset;
for (u32 i = 0; i < handlers.count; i++) {
const HBCExceptionHandler *handler = &handlers.handlers[i];
if (handler->start >= handler->end || handler->end > function.size || handler->target >= function.size) {
continue;
}
RBinTrycatch *tc = r_bin_trycatch_new (source,
source + handler->start,
source + handler->end,
source + handler->target,
0);
if (tc) {
r_list_append (ret, tc);
}
}
hbc_free_exception_handlers (&handlers);
}
return ret;
}
#endif
static ut64 baddr(RBinFile *bf R_UNUSED) {
return HBC_VADDR_BASE;
}
static void set_symbol_size(RBinSymbol *symbol, ut32 size) {
#if R2_ABIVERSION >= 134
symbol->attr.size = size;
#else
symbol->size = size;
#endif
}
static void set_function_signature(RBinSymbol *symbol, const HBCFunc *func) {
#if R2_ABIVERSION >= 100
/* LoadParam indexes the Hermes frame arguments; they are not registers. */
if (func->param_count <= UT16_MAX) {
symbol->cc_arg_count = func->param_count;
}
symbol->ret_count = 1;
#else
(void)symbol;
(void)func;
#endif
}
static void append_binding_symbols(RList *symbols, HBC *hbc) {
HBCBindings bindings = { 0 };
if (hbc_scan_bindings (hbc, &bindings).code != RESULT_SUCCESS) {
@@ -265,7 +369,7 @@ static void append_binding_symbols(RList *symbols, HBC *hbc) {
r_bin_name_filtered (sym->name, nm);
sym->paddr = b->offset;
sym->vaddr = HBC_VADDR_BASE + b->offset;
sym->size = 1;
set_symbol_size (sym, 1);
sym->ordinal = b->string_id;
sym->type = R_BIN_TYPE_OBJECT_STR;
sym->bind = R_BIN_BIND_GLOBAL_STR;
@@ -319,7 +423,16 @@ static RList *symbols(RBinFile *bf) {
symbol->paddr = fi.offset;
symbol->vaddr = HBC_VADDR_BASE + fi.offset;
symbol->size = fi.size;
set_symbol_size (symbol, fi.size);
set_function_signature (symbol, &fi);
#if R2_ABIVERSION >= 139
if (fi.flags & HBC_FUNCTION_ASYNC) {
symbol->attr.flags |= R_BIN_ATTR_ASYNC;
}
if (fi.flags & HBC_FUNCTION_GENERATOR) {
symbol->attr.flags |= R_BIN_ATTR_GENERATOR;
}
#endif
symbol->ordinal = i;
symbol->type = R_BIN_TYPE_FUNC_STR;
symbol->bits = 32;
@@ -348,7 +461,7 @@ static RList *symbols(RBinFile *bf) {
r_bin_name_filtered (sym->name, nm);
sym->paddr = groups[i].paddr;
sym->vaddr = HBC_VADDR_BASE + groups[i].paddr;
sym->size = 1;
set_symbol_size (sym, 1);
sym->type = R_BIN_TYPE_OBJECT_STR;
sym->bits = 32;
r_list_append (symbols, sym);
@@ -451,9 +564,6 @@ 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;
@@ -512,6 +622,7 @@ static RList *strings_list(RBinFile *bf) {
ptr->vaddr = HBC_VADDR_BASE + meta.offset;
ptr->size = meta.isUTF16? (meta.length * 2): meta.length;
ptr->length = meta.length;
ptr->type = meta.isUTF16? R_STRING_TYPE_WIDE: R_STRING_TYPE_UTF8;
ptr->ordinal = i;
r_list_append (ret, ptr);
}
@@ -519,6 +630,111 @@ 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) {
@@ -887,8 +1103,14 @@ 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
+170 -57
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* r2 core plugin for Hermes bytecode decompilation */
@@ -85,10 +85,7 @@ static Result hbc_load_current_binary(HbcContext *ctx, RCore *core) {
}
/* Open HBC from buffer */
Result res = hbc_open_from_buffer (bf->buf, &ctx->hbc);
if (res.code != RESULT_SUCCESS) {
return res;
}
RETURN_IF_ERROR (hbc_open_from_buffer (bf->buf, &ctx->hbc));
ctx->core = core;
ctx->file_path = strdup (file_path);
@@ -400,6 +397,7 @@ 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 {
@@ -408,7 +406,7 @@ static void cmd_list_eval_functions(HbcContext *ctx, RCore *core, const char *ar
address,
site->function_id,
site->offset,
HBC_VADDR_BASE + (ut64)site->function_address,
function_address,
site->strict? "true": "false",
r_str_get_fail (site->function_name, "unknown"));
}
@@ -743,7 +741,8 @@ static void register_r2_artifacts(RCore *core, const HBCLiteralEntry *e) {
if (!e->paddr) {
return;
}
ut64 vaddr = HBC_VADDR_BASE + (ut64)e->paddr;
ut64 base = r_config_get_b (core->config, "io.va")? HBC_VADDR_BASE: 0;
ut64 vaddr = base + e->paddr;
char flag_name[64];
snprintf (flag_name, sizeof (flag_name), "%s0x%x", lit_kind_prefix (e->kind), e->paddr);
r_flag_set (core->flags, flag_name, vaddr, 1);
@@ -751,7 +750,7 @@ static void register_r2_artifacts(RCore *core, const HBCLiteralEntry *e) {
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, vaddr, e->formatted);
}
for (u32 i = 0; i < e->xref_count; i++) {
ut64 from = HBC_VADDR_BASE + (ut64)e->xref_addrs[i];
ut64 from = base + e->xref_addrs[i];
r_anal_xrefs_set (core->anal, from, vaddr, R_ANAL_REF_TYPE_DATA);
}
}
@@ -799,17 +798,11 @@ static Result lit_list_collect(HbcContext *ctx, RCore *core, const HBCLiteralEnt
*out_arr = NULL;
*out_n = 0;
HBC *hbc = NULL;
Result r = ensure_hbc_loaded (ctx, core, &hbc);
if (r.code != RESULT_SUCCESS) {
return r;
}
RETURN_IF_ERROR (ensure_hbc_loaded (ctx, core, &hbc));
if (!ensure_lit_cache_populated (core, hbc)) {
return ERROR_RESULT (RESULT_ERROR_INVALID_DATA, "literal cache not populated");
}
r = hbc_literals_list (hbc, out_arr, out_n);
if (r.code != RESULT_SUCCESS) {
return r;
}
RETURN_IF_ERROR (hbc_literals_list (hbc, out_arr, out_n));
return SUCCESS_RESULT ();
}
@@ -820,9 +813,10 @@ static void cmd_lit_list_quiet(HbcContext *ctx, RCore *core) {
if (r.code != RESULT_SUCCESS) {
R_LOG_ERROR ("%s", safe_errmsg (r.error_message));
} else {
ut64 base = r_config_get_b (core->config, "io.va")? HBC_VADDR_BASE: 0;
for (u32 i = 0; i < n; i++) {
const HBCLiteralEntry *e = &arr[i];
const ut64 addr = (ut64)HBC_VADDR_BASE + e->paddr;
const ut64 addr = base + e->paddr;
const char *fmt = e->formatted? e->formatted: "";
r_cons_printf (core->cons, "0x%" PFMT64x " %s\n", addr, fmt);
}
@@ -853,18 +847,16 @@ static void cmd_lit_list_xrefs(HbcContext *ctx, RCore *core, const char *args) {
ut64 want = r_num_get (core->num, args);
ut64 base = va? (ut64)HBC_VADDR_BASE: 0;
ut64 target_paddr = (want >= base)? (want - base): want;
const HBCLiteralEntry *match = NULL;
bool found = false;
for (u32 i = 0; i < n; i++) {
if (arr[i].paddr == target_paddr) {
match = &arr[i];
break;
print_xref_line (core, &arr[i], va);
found = true;
}
}
if (!match) {
if (!found) {
R_LOG_ERROR ("no literal at that address");
return;
}
print_xref_line (core, match, va);
return;
}
for (u32 i = 0; i < n; i++) {
@@ -912,19 +904,23 @@ static void cmd_lit_list(HbcContext *ctx, RCore *core, bool as_json) {
}
r_cons_printf (core->cons, "literals: %u\n", n);
bool va = r_config_get_b (core->config, "io.va");
ut64 base = va? HBC_VADDR_BASE: 0;
for (u32 i = 0; i < n; i++) {
const HBCLiteralEntry *e = &arr[i];
const char *pavastr = va? "vaddr": "paddr";
r_cons_printf (core->cons,
"%-6s n=%-4u id=(%u,%u) %s=0x%08" PFMT64x " xrefs=%u %s\n",
"%-6s n=%-4u id=(%u,%u) %s=0x%08" PFMT64x " xrefs=%u",
kind_label (e->kind),
e->num_items,
e->primary_id,
e->secondary_id,
pavastr,
(ut64)HBC_VADDR_BASE + e->paddr,
e->xref_count,
e->formatted? e->formatted: "");
base + e->paddr,
e->xref_count);
for (u32 j = 0; j < e->xref_count; j++) {
r_cons_printf (core->cons, " 0x%" PFMT64x, base + e->xref_addrs[j]);
}
r_cons_printf (core->cons, " %s\n", e->formatted? e->formatted: "");
}
}
@@ -965,18 +961,16 @@ static void print_r2_for_entry(RCore *core, const HBCLiteralEntry *e, bool va) {
r_cons_printf (core->cons, "f %s0x%x 1 @ 0x%" PFMT64x "\n", prefix, e->paddr, vaddr);
/* embed the formatted text as a comment at the literal vaddr */
if (e->formatted && *e->formatted) {
char *clean = r_str_sanitize_r2 (e->formatted);
if (clean) {
r_str_replace_char (clean, '\n', ' ');
r_str_replace_char (clean, '\r', ' ');
r_cons_printf (core->cons, "\" /* %s */ CC %s @ 0x%" PFMT64x "\n", prefix, clean, vaddr);
free (clean);
char *b64 = r_base64_encode_dyn ((const ut8 *)e->formatted, -1);
if (b64) {
r_cons_printf (core->cons, "CCu base64:%s @ 0x%" PFMT64x "\n", b64, vaddr);
free (b64);
}
}
/* xref from each call site to the literal */
for (u32 j = 0; j < e->xref_count; j++) {
ut64 from = base + (ut64)e->xref_addrs[j];
r_cons_printf (core->cons, "axd 0x%" PFMT64x " 0x%" PFMT64x "\n", from, vaddr);
r_cons_printf (core->cons, "axd 0x%" PFMT64x " 0x%" PFMT64x "\n", vaddr, from);
}
}
@@ -998,18 +992,16 @@ static void cmd_lit_print_r2(HbcContext *ctx, RCore *core, const char *args) {
ut64 want = r_num_get (core->num, args);
ut64 base = va? (ut64)HBC_VADDR_BASE: 0;
ut64 target_paddr = (want >= base)? (want - base): want;
const HBCLiteralEntry *match = NULL;
bool found = false;
for (u32 i = 0; i < n; i++) {
if (arr[i].paddr == target_paddr) {
match = &arr[i];
break;
print_r2_for_entry (core, &arr[i], va);
found = true;
}
}
if (!match) {
if (!found) {
R_LOG_ERROR ("no literal at that address");
return;
}
print_r2_for_entry (core, match, va);
return;
}
for (u32 i = 0; i < n; i++) {
@@ -1066,29 +1058,33 @@ static void cmd_lit_toggle_inline(RCore *core) {
/* Show help */
static const char r2hermes_helpmsg[] =
"Usage: r2hermes-L[subcmd]\n"
" r2hermes-L List cached literals (auto-scans code if cache is empty)\n"
" r2hermes-L List cached literals and constructor addresses (auto-scans if empty)\n"
" r2hermes-Lj List as JSON (auto-scans if needed)\n"
" r2hermes-Lq List as `<vaddr> <formatted>` per line (one per literal)\n"
" r2hermes-Lx [addr] List as `<vaddr> <xref1> <xref2> ...` (one per literal)\n"
" With addr: print xrefs for that single literal only\n"
" r2hermes-Lq List as `<addr> <formatted>` per line (addresses use io.va)\n"
" r2hermes-Lx [addr] List as `<addr> <xref1> <xref2> ...` (addresses use io.va)\n"
" With addr: print all literals sharing that pool address\n"
" r2hermes-Lp[ao] Scan SLP pool (default: arrays; a=arrays, o=objects)\n"
" r2hermes-LR Reset literal cache (does not remove r2 flags/comments)\n"
" r2hermes-Lr [addr] Print r2 oneliners (f/CC/axd) to register flag+comment+xrefs\n"
" With addr: print oneliners for that single literal only\n"
" With addr: print all literals sharing that pool address\n"
" r2hermes-Lg <k> <n> <primary> [<sec>]\n"
" Format a literal from raw params (k=a|o)\n"
" r2hermes-Li Toggle inline literal comments in disasm\n"
" r2hermes-L? This help\n"
"\nDeprecated alias: pd:hL[subcmd]\n";
" r2hermes-L? This help\n";
static void r2hermes_help(RCore *core) {
const char msg[] =
"Usage: r2hermes[-arg] # see also pd:h for decompilation\n"
"r2hermes-h - help message (same as r2hermes-?, see pd:h? too)\n"
"r2hermes-A - Import symbols and entrypoints\n"
"r2hermes-AA - Also import strings and SLP literal comments/xrefs\n"
"r2hermes-AAA - Also analyze bytecode references\n"
"r2hermes-AAAA - Also perform full analysis (aaa)\n"
"r2hermes-E[jq] - List direct eval instruction sites (j=JSON, q=addresses only)\n"
"r2hermes-H - Show file information and hash status\n"
"r2hermes-L[?] - SLP literal cache: list/scan/reset/format/toggle\n"
"r2hermes-S[jr?] - emit SBOM from SLP literals (j=CycloneDX JSON, r=raw input)\n";
"r2hermes-S[jr?] - emit SBOM from SLP literals (j=CycloneDX JSON, r=raw input)\n"
"r2hermes-V[j?] - Show bytecode and React Native versions (HBC or libhermes.so)\n";
r_cons_print (core->cons, msg);
}
@@ -1106,7 +1102,6 @@ static void cmd_help(RCore *core) {
" pd:ho [id] - Decompile with offsets (addresses) per statement\n"
" pd:hoa - Decompile all with offsets\n"
" pd:hs [id] - List source-line information\n"
" pd:hL[?] - Deprecated alias for r2hermes-L[?]\n"
" pd:h? - Show this help\n"
"\nNote: r2 comments (CC command) are automatically inlined in decompiler output.\n");
}
@@ -1129,9 +1124,11 @@ 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;
@@ -1154,11 +1151,127 @@ static void cmd_literals(HbcContext *ctx, RCore *core, const char *arg) {
}
#include "sbom.inc.c"
#include "version.inc.c"
static void analysis_xref(RCore *core, ut64 from, ut64 target, RAnalRefType type, bool va) {
if (target == UT64_MAX || target < HBC_VADDR_BASE) {
return;
}
if (!va) {
target -= HBC_VADDR_BASE;
}
if (r_io_is_valid_offset (core->io, target, 0)) {
r_anal_xrefs_set (core->anal, from, target, type);
}
}
/* Decode complete functions, including tails shorter than the maximum opcode
* size that the generic aar scan skips. Decode at vaddrs so absolute function
* and string operands from the architecture plugin share one address space. */
static void analyze_function_refs(RCore *core, HBC *hbc, u32 id, bool va) {
HBCFunc fi;
const u8 *code = NULL;
u32 size = 0;
if (hbc_get_function_info (hbc, id, &fi).code != RESULT_SUCCESS ||
hbc_get_function_bytecode (hbc, id, &code, &size).code != RESULT_SUCCESS || !code) {
return;
}
for (u32 pc = 0; pc < size && !r_cons_is_breaked (core->cons);) {
const ut64 vaddr = HBC_VADDR_BASE + fi.offset + pc;
const ut64 from = va? vaddr: (ut64)fi.offset + pc;
RAnalOp op = { 0 };
const int len = r_anal_op (core->anal, &op, vaddr, code + pc, R_MIN (size - pc, INT_MAX), R_ARCH_OP_MASK_BASIC);
if (len < 1 || (u32)len > size - pc) {
r_anal_op_fini (&op);
break;
}
analysis_xref (core, from, op.ptr, R_ANAL_REF_TYPE_DATA, va);
if (op.type == R_ANAL_OP_TYPE_CALL || op.type == R_ANAL_OP_TYPE_CCALL) {
analysis_xref (core, from, op.jump, R_ANAL_REF_TYPE_CALL, va);
} else if (op.type == R_ANAL_OP_TYPE_JMP || op.type == R_ANAL_OP_TYPE_CJMP) {
analysis_xref (core, from, op.jump, R_ANAL_REF_TYPE_CODE, va);
}
r_anal_op_fini (&op);
pc += len;
}
}
static void cmd_analyze(RCore *core, HbcContext *ctx, const char *arg) {
unsigned level = 0;
while (arg[level] == 'A') {
level++;
}
const char *rest = r_str_trim_head_ro (arg + level);
if (!strcmp (rest, "?")) {
r_cons_print (core->cons,
"Usage: r2hermes-A[AAA]\n"
" r2hermes-A Import symbols and entrypoints\n"
" r2hermes-AA Also import strings and SLP literal flags/comments/xrefs\n"
" r2hermes-AAA Also analyze references in every bytecode function\n"
" r2hermes-AAAA Also perform full analysis (aaa)\n"
"Levels are cumulative; addresses follow io.va.\n");
return;
}
if (level > 4 || *rest) {
R_LOG_ERROR ("Invalid analysis level. Use r2hermes-A? for help");
return;
}
HBC *hbc = NULL;
Result res = ensure_hbc_loaded (ctx, core, &hbc);
if (res.code != RESULT_SUCCESS) {
R_LOG_ERROR ("%s", safe_errmsg (res.error_message));
return;
}
const ut64 addr = core->addr;
const bool va = r_config_get_b (core->config, "io.va");
if (!r_flag_space_push (core->flags, R_FLAGS_FS_SYMBOLS)) {
return;
}
if (!r_core_bin_info (core, R_CORE_BIN_ACC_ENTRIES, NULL, R_MODE_SET, va, NULL, NULL) ||
r_core_cmd0 (core, ".is*") != 0) {
R_LOG_ERROR ("Failed to import HBC symbols and entrypoints");
goto done;
}
if (level >= 2) {
if (!r_core_bin_info (core, R_CORE_BIN_ACC_STRINGS, NULL, R_MODE_SET, va, NULL, NULL)) {
R_LOG_ERROR ("Failed to import HBC strings");
goto done;
}
res = hbc_literals_scan_code (hbc, NULL);
if (res.code != RESULT_SUCCESS) {
R_LOG_ERROR ("Literal scan failed: %s", safe_errmsg (res.error_message));
goto done;
}
r_flag_space_set (core->flags, R_FLAGS_FS_SYMBOLS);
register_all_artifacts (core, hbc);
}
if (level >= 3) {
const u32 count = hbc_function_count (hbc);
r_cons_break_push (core->cons, NULL, NULL);
for (u32 i = 0; i < count && !r_cons_is_breaked (core->cons); i++) {
analyze_function_refs (core, hbc, i, va);
}
const bool interrupted = r_cons_is_breaked (core->cons);
r_cons_break_pop (core->cons);
if (interrupted) {
goto done;
}
}
if (level >= 4) {
r_core_cmd0 (core, "aaa");
}
done:
r_flag_space_pop (core->flags);
r_core_seek (core, addr, true);
}
static void cmd_r2hermes(RCore *core, HbcContext *ctx, const char *arg) {
arg = r_str_trim_head_ro (arg);
if (*arg == '-') {
switch (arg[1]) {
case 'A':
cmd_analyze (core, ctx, arg + 1);
break;
case 'E':
cmd_list_eval_functions (ctx, core, arg + 2);
break;
@@ -1171,6 +1284,9 @@ static void cmd_r2hermes(RCore *core, HbcContext *ctx, const char *arg) {
case 'S':
cmd_sbom (ctx, core, arg + 2);
break;
case 'V':
cmd_version (ctx, core, arg + 2);
break;
case '?':
case 'h':
r2hermes_help (core);
@@ -1228,10 +1344,6 @@ static void cmd_pdh(RCore *core, HbcContext *ctx, const char *arg) {
}
break;
}
case 'L': /* pd:hL* — deprecated alias for r2hermes-L* */
R_LOG_WARN ("pd:hL is deprecated; use r2hermes-L instead");
cmd_literals (ctx, core, arg + 1);
break;
case 's': /* pd:hs [id] */
cmd_source_lines (ctx, core, arg + 1);
break;
@@ -1400,6 +1512,7 @@ R_API RLibStruct radare_plugin = {
.type = R_LIB_TYPE_CORE,
.data = (void *)&r_core_plugin_r2hermes,
.version = R2_VERSION,
.abiversion = R2_ABIVERSION
.abiversion = R2_ABIVERSION,
.pkgname = "r2hermes"
};
#endif
+1 -1
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
/* Core HBC plugin with plugin registration enabled for r2one */
+14 -14
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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 ("==")
case OP_StrictEq: E3 ("==,$z")
case OP_Neq:
case OP_StrictNeq: E3 ("==,!")
case OP_StrictNeq: E3 ("==,$z,!")
case OP_Less: E3 ("<")
case OP_Greater: E3 (">")
case OP_LessEq: E3 ("<=")
@@ -184,10 +184,10 @@ static void set_esil(RAnalOp *op, const u8 *bytes, ut64 addr) {
r_strbuf_setf (&op->esil, "%u,r%u,=", r_read_le32 (bytes + 2), bytes[1]);
break;
case OP_LoadParam:
r_strbuf_setf (&op->esil, "arg%u,r%u,=", bytes[2], bytes[1]);
r_strbuf_setf (&op->esil, "%u,HBC_LOAD_PARAM,r%u,=", bytes[2], bytes[1]);
break;
case OP_LoadParamLong:
r_strbuf_setf (&op->esil, "arg%u,r%u,=", r_read_le32 (bytes + 2), bytes[1]);
r_strbuf_setf (&op->esil, "%u,HBC_LOAD_PARAM,r%u,=", r_read_le32 (bytes + 2), bytes[1]);
break;
case OP_NewArray:
r_strbuf_setf (&op->esil, "%u,NEWARR,r%u,=", r_read_le16 (bytes + 2), bytes[1]);
@@ -229,7 +229,7 @@ static void set_esil(RAnalOp *op, const u8 *bytes, ut64 addr) {
r_strbuf_setf (&op->esil, "r%u,THROW", bytes[1]);
break;
case OP_ThrowIfUndefinedInst:
r_strbuf_setf (&op->esil, "r%u,UNDEFINED,==,?{,THROW,}", bytes[1]);
r_strbuf_setf (&op->esil, "r%u,UNDEFINED,==,$z,?{,THROW,}", bytes[1]);
break;
case OP_IteratorClose:
r_strbuf_setf (&op->esil, "r%u,ITERCLOSE", bytes[1]);
@@ -276,14 +276,14 @@ static void set_esil(RAnalOp *op, const u8 *bytes, ut64 addr) {
case OP_JNotGreaterEqualN: JCC_S ("<")
case OP_JNotGreaterEqualLong:
case OP_JNotGreaterEqualNLong: JCC_L ("<")
case OP_JEqual: JCC_S ("==")
case OP_JEqualLong: JCC_L ("==")
case OP_JNotEqual: JCC_S ("==,!")
case OP_JNotEqualLong: JCC_L ("==,!")
case OP_JStrictEqual: JCC_S ("==")
case OP_JStrictEqualLong: JCC_L ("==")
case OP_JStrictNotEqual: JCC_S ("==,!")
case OP_JStrictNotEqualLong: JCC_L ("==,!")
case OP_JEqual: JCC_S ("==,$z")
case OP_JEqualLong: JCC_L ("==,$z")
case OP_JNotEqual: JCC_S ("==,$z,!")
case OP_JNotEqualLong: JCC_L ("==,$z,!")
case OP_JStrictEqual: JCC_S ("==,$z")
case OP_JStrictEqualLong: JCC_L ("==,$z")
case OP_JStrictNotEqual: JCC_S ("==,$z,!")
case OP_JStrictNotEqualLong: JCC_L ("==,$z,!")
/* aggregate / opaque ops with no precise ESIL model */
case OP_Unreachable:
+2 -2
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - 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;
+2 -16
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#ifndef R2HERMES_UTILS_INC_C
#define R2HERMES_UTILS_INC_C
@@ -9,21 +9,7 @@ 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");
}
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;
return hbc_open_from_rbuffer (buf, out_hbc);
}
static inline void hbc_safe_close(HBC **hbc) {
+145
View File
@@ -0,0 +1,145 @@
/* r2hermes - BSD - Copyright 2026 - pancake */
/* r2hermes-V: identify the Hermes / React Native build of the loaded binary.
*
* For HBC files the bytecode version comes from the header. For the native
* engine (libhermes.so) it recovers:
* - the "for RN x.y.z" string Hermes reports as "OSS Release Version"
* - the constant returned by HermesRuntime::getBytecodeVersion ()
* - the toolchain recorded in .comment (via RBinInfo.compiler)
*/
#include <hbc/parser.h>
#define HERMES_RN_TAG "for RN "
#define HERMES_BCVER_SYM "_ZN8facebook6hermes13HermesRuntime18getBytecodeVersionEv"
static char *version_find_rn(RBinFile *bf) {
ut64 size = 0;
const ut8 *data = r_buf_data (bf->buf, &size);
if (!data || size > ST32_MAX) {
return NULL;
}
const int taglen = strlen (HERMES_RN_TAG);
const ut8 *p = data;
const ut8 *end = data + size;
while ((p = r_mem_mem (p, (int) (end - p), (const ut8 *)HERMES_RN_TAG, taglen))) {
const ut8 *v = p + taglen;
const ut8 *q = v;
while (q < end && (isdigit (*q) || *q == '.' || *q == '-' || isalpha (*q))) {
q++;
}
// must be a standalone NUL-terminated string starting with a digit
if (q > v && q < end && !*q && isdigit (*v) && (p == data || !p[-1])) {
return r_str_ndup ((const char *)v, (int) (q - v));
}
p++;
}
return NULL;
}
static int version_find_bcver(RCore *core) {
RVecRBinSymbol *syms = r_bin_get_symbols_vec (core->bin);
if (!syms) {
return -1;
}
ut64 addr = UT64_MAX;
RBinSymbol *sym;
R_VEC_FOREACH (syms, sym) {
if (sym->name && sym->vaddr && r_str_startswith (r_str_get (sym->name->oname), HERMES_BCVER_SYM)) {
addr = sym->vaddr;
break;
}
}
if (addr == UT64_MAX) {
return -1;
}
ut8 buf[64];
if (!r_io_read_at (core->io, addr, buf, sizeof (buf))) {
return -1;
}
// the function just returns a constant; accept a short prologue before it
int ver = -1;
int off = 0;
int i;
for (i = 0; i < 8 && off < (int)sizeof (buf); i++) {
RAnalOp op;
r_anal_op_init (&op);
int len = r_anal_op (core->anal, &op, addr + off, buf + off, sizeof (buf) - off, R_ARCH_OP_MASK_VAL);
const ut32 type = op.type & R_ANAL_OP_TYPE_MASK;
if (type == R_ANAL_OP_TYPE_MOV && op.val != UT64_MAX && op.val > 0 && op.val < 1000) {
ver = (int)op.val;
}
r_anal_op_fini (&op);
if (len < 1 || type == R_ANAL_OP_TYPE_RET) {
break;
}
off += len;
}
return ver;
}
static void cmd_version(HbcContext *ctx, RCore *core, const char *arg) {
const bool json = *arg == 'j';
if (*arg && !json) {
r_cons_print (core->cons,
"Usage: r2hermes-V[j]\n"
" r2hermes-V Show Hermes bytecode and React Native versions\n"
" r2hermes-Vj Same as JSON\n");
return;
}
RBinFile *bf = core->bin->cur;
if (!bf || !bf->buf) {
R_LOG_ERROR ("No binary loaded");
return;
}
int bcver = -1;
char *rn = NULL;
const char *compiler = NULL;
const char *kind = "native";
HBCHeader header;
ut8 magic[8];
if (r_buf_read_at (bf->buf, 0, magic, sizeof (magic)) == sizeof (magic) && r_read_le64 (magic) == HEADER_MAGIC && hbc_load_current_binary (ctx, core).code == RESULT_SUCCESS && hbc_get_header (ctx->hbc, &header).code == RESULT_SUCCESS) {
kind = "hbc";
bcver = header.version;
} else {
rn = version_find_rn (bf);
bcver = version_find_bcver (core);
RBinInfo *info = r_bin_get_info (core->bin);
compiler = info? info->compiler: NULL;
if (!rn && bcver < 0) {
R_LOG_WARN ("This does not look like a Hermes binary");
}
}
RCons *cons = core->cons;
if (json) {
PJ *pj = r_core_pj_new (core);
pj_o (pj);
pj_ks (pj, "type", kind);
if (bcver >= 0) {
pj_kn (pj, "bytecode_version", bcver);
}
if (rn) {
pj_ks (pj, "react_native", rn);
}
if (compiler) {
pj_ks (pj, "compiler", compiler);
}
pj_end (pj);
char *s = pj_drain (pj);
r_cons_println (cons, s);
free (s);
} else {
r_cons_printf (cons, "type %s\n", kind);
if (bcver >= 0) {
r_cons_printf (cons, "bytecode_version %d\n", bcver);
}
if (rn) {
r_cons_printf (cons, "react_native %s\n", rn);
}
if (compiler) {
r_cons_printf (cons, "compiler %s\n", compiler);
}
}
free (rn);
}
+4 -14
View File
@@ -1,4 +1,4 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include <hbc/common.h>
#include <hbc/hbc.h>
@@ -57,17 +57,7 @@ static Result errorf(ResultCode code, const char *fmt, ...) {
va_start (ap, fmt);
vsnprintf (buf, sizeof (buf), fmt, ap);
va_end (ap);
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);
return (Result){ code, buf };
}
static bool streq(const char *a, const char *b) {
@@ -1207,7 +1197,7 @@ int main(int argc, char **argv) {
const Command *cmd = find_command (argv[1]);
if (!cmd) {
print_usage (ctx.program_name);
eprintf ("Unknown command: %s", argv[1]);
fprintf (stderr, "Error: Unknown command: %s\n", argv[1]);
return 1;
}
int rest_argc = argc - 2;
@@ -1217,7 +1207,7 @@ int main(int argc, char **argv) {
r = cmd->handler (&ctx, rest_argc, rest_argv);
}
if (r.code != RESULT_SUCCESS) {
eprintf ("%s", r.error_message[0]? r.error_message: "Unknown error");
fprintf (stderr, "Error: %s\n", r.error_message[0]? r.error_message: "Unknown error");
return 1;
}
return 0;
+161
View File
@@ -0,0 +1,161 @@
NAME=r2hermes analysis help
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
r2hermes-?~r2hermes-A
r2hermes-A?
EOF
EXPECT=<<EOF
r2hermes-A - Import symbols and entrypoints
r2hermes-AA - Also import strings and SLP literal comments/xrefs
r2hermes-AAA - Also analyze bytecode references
r2hermes-AAAA - Also perform full analysis (aaa)
Usage: r2hermes-A[AAA]
r2hermes-A Import symbols and entrypoints
r2hermes-AA Also import strings and SLP literal flags/comments/xrefs
r2hermes-AAA Also analyze references in every bytecode function
r2hermes-AAAA Also perform full analysis (aaa)
Levels are cumulative; addresses follow io.va.
EOF
RUN
NAME=r2hermes-A imports symbols and preserves session state
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
f-*
fs scratch
s 0x100000b4
r2hermes-A
?v $$
fs~scratch[1]
fs *
f~global,entry,str.
EOF
EXPECT=<<EOF
0x100000b4
*
0x100000b0 1 entry0
0x100000b0 10 sym.global_0x000000b0
EOF
RUN
NAME=r2hermes-AA adds strings
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
f-*
r2hermes-AA
f~global,entry,str.
EOF
EXPECT=<<EOF
0x1000009c 6 str.global
0x100000a2 11 str.print_123__
0x100000b0 1 entry0
0x100000b0 10 sym.global_0x000000b0
EOF
RUN
NAME=r2hermes-AA imports literals again from an existing cache
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
r2hermes-Lq~^0x10037b28
ax-*
f- lit.*
CC- @ 0x10037b28
r2hermes-AA
f~lit.obj.0x37b28
CC. @ 0x10037b28
axtq @ 0x10037b28
r2hermes-AA
axtq @ 0x10037b28
EOF
EXPECT=<<EOF
0x10037b28 {trace: 0, info: 1, warn: 2, error: 3}
0x10037b28 1 lit.obj.0x37b28
{trace: 0, info: 1, warn: 2, error: 3}
0x1003e9f9
0x1003e9f9
EOF
RUN
NAME=r2hermes-AA imports literal physical addresses
FILE=bins/hbc/index.android.bundle
ARGS=-e io.va=false
CMDS=<<EOF
f-*
r2hermes-AA
f~lit.obj.0x37b28
CC. @ 0x37b28
axtq @ 0x37b28
EOF
EXPECT=<<EOF
0x00037b28 1 lit.obj.0x37b28
{trace: 0, info: 1, warn: 2, error: 3}
0x3e9f9
EOF
RUN
NAME=r2hermes-AAA finds short function references and remains additive
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
ax-*
r2hermes-AA
axtq @ 0x100000a2
r2hermes-AAA
axtq @ 0x100000a2
r2hermes-AAA
axtq @ 0x100000a2
EOF
EXPECT=<<EOF
0x100000b0
0x100000b0
EOF
RUN
NAME=r2hermes-AAA uses physical addresses for code and data
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e io.va=false
CMDS=<<EOF
ax-*
r2hermes-AAA
axtq @ 0xa2
axtq @ 0x100000a2
f~global,entry,str.print
EOF
EXPECT=<<EOF
0xb0
0x0000009c 6 str.global
0x000000a2 11 str.print_123__
0x000000b0 1 entry0
0x000000b0 10 sym.global_0x000000b0
EOF
RUN
NAME=r2hermes-AAAA analyzes function control flow
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e log.level=0
CMDS=<<EOF
r2hermes-AAAA
aflj~{[0].addr}
aflj~{[0].size}
EOF
EXPECT=<<EOF
268435632
10
EOF
RUN
NAME=r2hermes analysis rejects invalid levels before importing
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
f-*
r2hermes-AAAAA
r2hermes-AAj
r2hermes-AA extra
f
EOF
EXPECT=
EXPECT_ERR=<<EOF
ERROR: Invalid analysis level. Use r2hermes-A? for help
ERROR: Invalid analysis level. Use r2hermes-A? for help
ERROR: Invalid analysis level. Use r2hermes-A? for help
EOF
RUN
+1 -3
View File
@@ -415,9 +415,7 @@ RUN
NAME=pd:hc short-circuit OR merges consecutive same-target conditional gotos
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
pd:hc 730~ || r2===r20 ||
EOF
CMDS=pd:hc 730~ \|\| r2===r20 \|\|
EXPECT=<<EOF
if (r1===r20 || r2===r20 || r3===r20 || r4===r20) goto loc_00055f28;
EOF
+115
View File
@@ -84,6 +84,77 @@ EXPECT=<<EOF
EOF
RUN
NAME=hbc function signature metadata
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e asm.sub.varmin=16
CMDS=<<EOF
isj~{[0].arg_count}
isj~{[0].cc_arg_count}
isj~{[0].ret_count}
EOF
EXPECT=<<EOF
0
1
1
EOF
RUN
NAME=hbc register profile has no fake ABI roles
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e asm.sub.varmin=16
CMDS=<<EOF
drpj~{alias_info}
EOF
EXPECT=<<EOF
[{"role":0,"role_str":"PC","reg":"pc"}]
EOF
RUN
NAME=hbc LoadParam ESIL uses a frame slot instead of a fake register
FILE=bins/hbc/index.android.bundle
ARGS=-e asm.sub.varmin=16
CMDS=<<EOF
aoj 1 @ 0x1003e3f7~{[0].esil}
EOF
EXPECT=<<EOF
1,HBC_LOAD_PARAM,r8,=
EOF
RUN
NAME=hbc equality branch ESIL materializes the zero comparison
FILE=bins/hbc/index.android.bundle
ARGS=-e asm.sub.varmin=16
CMDS=<<EOF
aoj 1 @ 0x1003e5fc~{[0].esil}
EOF
EXPECT=<<EOF
r1,r3,==,$z,!,?{,0x1003e637,pc,=,}
EOF
RUN
NAME=hbc SwitchImm analysis exposes jump table metadata
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
af @ 0x1004d09a
agfj @ 0x1004d09a~{[0].blocks[0].switchop}
EOF
EXPECT=<<EOF
{"addr":268751010,"defval":268751197,"maxval":27,"minval":0,"daddr":268751204,"dsize":4,"amount":28,"cases":[{"addr":268751204,"value":0,"jump":268751078},{"addr":268751208,"value":1,"jump":268751028},{"addr":268751212,"value":2,"jump":268751078},{"addr":268751224,"value":5,"jump":268751172},{"addr":268751248,"value":11,"jump":268751050},{"addr":268751256,"value":13,"jump":268751124},{"addr":268751264,"value":15,"jump":268751078},{"addr":268751268,"value":16,"jump":268751148},{"addr":268751280,"value":19,"jump":268751100},{"addr":268751308,"value":26,"jump":268751172},{"addr":268751312,"value":27,"jump":268751172}]}
EOF
RUN
NAME=hbc exception handlers are exposed through bin metadata
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
iw*~try.0
EOF
EXPECT=<<EOF
f try.0.1003e6dc.from=0x1003e703
f try.0.1003e6dc.to=0x1003e713
f try.0.1003e6dc.catch=0x1003e715
EOF
RUN
NAME=hbc entry point (va)
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e asm.sub.varmin=16
@@ -182,6 +253,50 @@ EXPECT=<<EOF
EOF
RUN
NAME=hbc structured operands
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
aoj 3 @ entry0~{0.opex}
aoj 3 @ entry0~{1.opex}
aoj 3 @ entry0~{2.opex}
EOF
EXPECT=<<EOF
{"operands":[{"type":"reg","value":"r0"},{"type":"imm","value":1,"kind":"string_id","address":268435618,"byte_length":11}]}
{"operands":[{"type":"reg","value":"r0"},{"type":"reg","value":"r0"},{"type":"imm","value":0}]}
{"operands":[{"type":"reg","value":"r0"}]}
EOF
RUN
NAME=hbc structured empty string length
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
aoj 1 @ 0x1003e45b~{0.opex}
EOF
EXPECT=<<EOF
{"operands":[{"type":"reg","value":"r9"},{"type":"imm","value":2976,"kind":"string_id","address":268560336,"byte_length":0}]}
EOF
RUN
NAME=hbc structured function operand
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
aoj 1 @ 0x1003a3bb~{0.opex}
EOF
EXPECT=<<EOF
{"operands":[{"type":"reg","value":"r1"},{"type":"reg","value":"r3"},{"type":"imm","value":1,"kind":"function_id","address":268690421}]}
EOF
RUN
NAME=hbc implicit call frame operands
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
aoj 1 @ 0x100895ac~{0.opex}
EOF
EXPECT=<<EOF
{"operands":[{"type":"reg","value":"r1"},{"type":"reg","value":"r5"},{"type":"imm","value":5},{"type":"reg","value":"r10","implicit":true,"kind":"call_this"},{"type":"reg","value":"r9","implicit":true,"kind":"call_arg","index":0},{"type":"reg","value":"r8","implicit":true,"kind":"call_arg","index":1},{"type":"reg","value":"r7","implicit":true,"kind":"call_arg","index":2},{"type":"reg","value":"r6","implicit":true,"kind":"call_arg","index":3}]}
EOF
RUN
NAME=hbc sections (va)
FILE=bins/hbc/bespoke_eval.hbc
ARGS=-e asm.sub.varmin=16
+13
View File
@@ -81,3 +81,16 @@ EXPECT=<<EOF
{"magic":2240826417119764422,"version":96,"sourceHash":"a97c8302dab90bec7184a9183b22b43badd57a65","fileLength":292,"globalCodeIndex":0,"functionCount":1,"stringKindCount":1,"identifierCount":0,"stringCount":2,"overflowStringCount":0,"stringStorageSize":17,"bigIntCount":0,"bigIntStorageSize":0,"regExpCount":0,"regExpStorageSize":0,"literalValueBufferSize":0,"arrayBufferSize":0,"objKeyBufferSize":0,"objValueBufferSize":0,"objShapeTableCount":0,"numStringSwitchImms":0,"segmentID":0,"cjsModuleCount":0,"functionSourceCount":0,"debugInfoOffset":200,"staticBuiltins":false,"cjsModulesStaticallyResolved":false,"hasAsync":false}
EOF
RUN
NAME=r2hermes-V reports the HBC bytecode version
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
r2hermes-V
r2hermes-Vj
EOF
EXPECT=<<EOF
type hbc
bytecode_version 96
{"type":"hbc","bytecode_version":96}
EOF
RUN
+59
View File
@@ -0,0 +1,59 @@
NAME=hbc literal addresses and constructor refs
FILE=bins/hbc/index.android.bundle
CMDS=<<EOF
r2hermes-L~trace:
r2hermes-Lx 0x10037b3c
r2hermes-Lr 0x10037b3c~^axd
ax-*
.r2hermes-Lr 0x10037b3c
axtq @ 0x10037b3c
EOF
EXPECT=<<EOF
object n=4 id=(0,0) vaddr=0x10037b28 xrefs=1 0x1003e9f9 {trace: 0, info: 1, warn: 2, error: 3}
0x10037b3c 0x1003eb09 0x1003ebe7
0x10037b3c 0x100c2cfb
axd 0x10037b3c 0x1003eb09
axd 0x10037b3c 0x1003ebe7
axd 0x10037b3c 0x100c2cfb
0x1003eb09
0x1003ebe7
0x100c2cfb
EOF
RUN
NAME=hbc literal physical addresses and refs
FILE=bins/hbc/index.android.bundle
ARGS=-e io.va=false
CMDS=<<EOF
r2hermes-L~trace:
r2hermes-Lq~^0x37b28
r2hermes-Lx 0x37b3c
r2hermes-Lr 0x37b3c~^axd
axtq @ 0x37b3c
EOF
EXPECT=<<EOF
object n=4 id=(0,0) paddr=0x00037b28 xrefs=1 0x3e9f9 {trace: 0, info: 1, warn: 2, error: 3}
0x37b28 {trace: 0, info: 1, warn: 2, error: 3}
0x37b3c 0x3eb09 0x3ebe7
0x37b3c 0xc2cfb
axd 0x37b3c 0x3eb09
axd 0x37b3c 0x3ebe7
axd 0x37b3c 0xc2cfb
0x3eb09
0x3ebe7
0xc2cfb
EOF
RUN
NAME=hbc literal help drops deprecated alias
FILE=bins/hbc/bespoke_eval.hbc
CMDS=<<EOF
r2hermes-L?~pd:hL
pd:h?~pd:hL
pd:hL?
EOF
EXPECT=
EXPECT_ERR=<<EOF
ERROR: Unknown subcommand. Use pd:h? for help
EOF
RUN
+1
View File
@@ -8,6 +8,7 @@ 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()])
+127 -4
View File
@@ -1,6 +1,7 @@
/* radare2 - BSD - Copyright 2025-2026 - pancake */
/* r2hermes - BSD - Copyright 2025-2026 - pancake */
#include "../src/lib/hbc_internal.h"
#include <hbc/opcodes.h>
#include <stdio.h>
#include <string.h>
@@ -18,6 +19,9 @@ static int test_small_debug_info(const char *root) {
HBC *hbc = NULL;
CHECK (hbc_open (path, &hbc).code == RESULT_SUCCESS);
u32 frame_size = 0;
CHECK (hbc_get_function_frame_size (hbc, 0, &frame_size).code == RESULT_SUCCESS);
CHECK (frame_size == hbc->reader.function_headers[0].frameSize);
HBCDebugInfo di = { 0 };
CHECK (hbc_get_debug_info (hbc, &di).code == RESULT_SUCCESS);
@@ -74,6 +78,81 @@ static int test_empty_debug_info(const char *root) {
return 0;
}
static int test_exception_handlers(const char *root) {
char path[512];
snprintf (path, sizeof (path), "%s/test/bins/hbc/index.android.bundle", root);
HBC *hbc = NULL;
CHECK (hbc_open (path, &hbc).code == RESULT_SUCCESS);
u32 handler_count = 0;
u32 generator_count = 0;
for (u32 function_id = 0; function_id < hbc_function_count (hbc); function_id++) {
HBCFunc function;
CHECK (hbc_get_function_info (hbc, function_id, &function).code == RESULT_SUCCESS);
CHECK (!(function.flags & HBC_FUNCTION_ASYNC));
if (function.flags & HBC_FUNCTION_GENERATOR) {
generator_count++;
}
HBCExceptionHandlerArray handlers = { 0 };
CHECK (hbc_get_function_exception_handlers (hbc, function_id, &handlers).code == RESULT_SUCCESS);
for (u32 i = 0; i < handlers.count; i++) {
CHECK (handlers.handlers[i].start < handlers.handlers[i].end);
CHECK (handlers.handlers[i].end <= function.size);
CHECK (handlers.handlers[i].target < function.size);
}
handler_count += handlers.count;
hbc_free_exception_handlers (&handlers);
}
CHECK (handler_count > 0);
CHECK (generator_count > 0);
hbc_close (hbc);
return 0;
}
static const Instruction *find_instruction(HBCISA isa, const char *name) {
for (u32 i = 0; i < isa.count; i++) {
if (isa.instructions[i].name && !strcmp (isa.instructions[i].name, name)) {
return &isa.instructions[i];
}
}
return NULL;
}
static int test_opcode_operand_meanings(void) {
static const int versions[] = {
51, 58, 59, 61, 62, 68, 69, 70, 72, 73, 76, 80, 81, 82,
83, 84, 85, 86, 87, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99
};
static const char *function_ops[] = {
"call_direct", "call_direct_long_index",
"create_closure", "create_closure_long_index",
"create_generator_closure", "create_generator_closure_long_index",
"create_async_closure", "create_async_closure_long_index",
"create_generator", "create_generator_long_index"
};
static const char *builtin_ops[] = {
"call_builtin", "call_builtin_long", "get_builtin_closure",
"jmp_builtin_is", "jmp_builtin_is_long",
"jmp_builtin_is_not", "jmp_builtin_is_not_long"
};
for (size_t i = 0; i < sizeof (versions) / sizeof (versions[0]); i++) {
HBCISA isa = hbc_isa_getv (versions[i]);
for (size_t j = 0; j < sizeof (function_ops) / sizeof (function_ops[0]); j++) {
const Instruction *insn = find_instruction (isa, function_ops[j]);
if (insn) {
CHECK (insn->operands[2].operand_meaning == OPERAND_MEANING_FUNCTION_ID);
}
}
for (size_t j = 0; j < sizeof (builtin_ops) / sizeof (builtin_ops[0]); j++) {
const Instruction *insn = find_instruction (isa, builtin_ops[j]);
if (insn) {
CHECK (insn->operands[1].operand_meaning == OPERAND_MEANING_BUILTIN_ID);
}
}
}
return 0;
}
static int test_function_bytecode_bounds(const char *root) {
char path[512];
snprintf (path, sizeof (path), "%s/test/bins/hbc/bespoke_eval.hbc", root);
@@ -81,10 +160,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 (hbc->reader.file_buffer.size < UINT32_MAX);
CHECK (r_buf_size (hbc->reader.file_buffer) < UINT32_MAX);
FunctionHeader *fh = &hbc->reader.function_headers[0];
fh->offset = (u32)hbc->reader.file_buffer.size + 1;
fh->offset = (u32)r_buf_size (hbc->reader.file_buffer) + 1;
fh->bytecodeSizeInBytes = 1;
const u8 *ptr = (const u8 *)1;
@@ -130,9 +209,53 @@ static int test_decode_rejects_bad_overflow_string_index(void) {
return 0;
}
static int test_literal_addresses(void) {
u8 keys[] = { 0x71, 1, 0, 0, 0 };
u8 values[] = { 0x71, 20, 0, 0, 0, 0x71, 36, 0, 0, 0 };
ShapeTableEntry shape = { .key_buffer_offset = 0, .prop_count = 1 };
for (u32 version = 96; version <= 97; version++) {
HBC hbc = { 0 };
RVecHBCLiteralEntry_init (&hbc.lit_cache.entries);
hbc.reader.header.version = version;
hbc.reader.header.arrayBufferSize = sizeof (values);
hbc.reader.header.objKeyBufferSize = sizeof (keys);
hbc.reader.header.objValueBufferSize = sizeof (values);
hbc.reader.arrays = values;
hbc.reader.arrays_paddr = 0x100;
hbc.reader.object_keys = keys;
hbc.reader.object_keys_paddr = 0x200;
hbc.reader.object_values = values;
hbc.reader.object_values_paddr = version >= 97? 0x100: 0x300;
hbc.reader.object_shapes = &shape;
hbc.reader.object_shape_count = 1;
u32 num_items = version >= 97? 0: 1;
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 0, 0x400).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 5, 0x410).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 5, 0x420).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_ARRAY, 1, 5, 0, 0x430).code == RESULT_SUCCESS);
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, sizeof (values), 0x440).code == RESULT_SUCCESS);
const HBCLiteralEntry *entries = NULL;
u32 count = 0;
CHECK (hbc_literals_list (&hbc, &entries, &count).code == RESULT_SUCCESS);
CHECK (count == 4);
CHECK (entries[0].paddr == hbc.reader.object_values_paddr);
CHECK (entries[1].paddr == hbc.reader.object_values_paddr + 5);
CHECK (entries[2].paddr == 0x105);
CHECK (entries[3].paddr == 0);
CHECK (!strcmp (entries[0].formatted, "{1: 20}"));
CHECK (!strcmp (entries[1].formatted, "{1: 36}"));
CHECK (!strcmp (entries[2].formatted, "[36]"));
CHECK (entries[1].xref_count == 2);
CHECK (entries[1].xref_addrs[0] == 0x410);
CHECK (entries[1].xref_addrs[1] == 0x420);
hbc_literals_reset (&hbc);
}
return 0;
}
int main(int argc, char **argv) {
const char *root = argc > 1? argv[1]: ".";
if (test_small_debug_info (root) || test_empty_debug_info (root) || test_function_bytecode_bounds (root) || test_decode_rejects_bad_overflow_string_index ()) {
if (test_small_debug_info (root) || test_empty_debug_info (root) || test_exception_handlers (root) || test_opcode_operand_meanings () || test_function_bytecode_bounds (root) || test_decode_rejects_bad_overflow_string_index () || test_literal_addresses ()) {
return 1;
}
return 0;