mirror of
https://github.com/rizinorg/jsdec.git
synced 2026-11-07 14:58:12 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8180b60b9a | ||
|
|
1903c99b3a | ||
|
|
65821969c1 | ||
|
|
ac88882529 | ||
|
|
055f537948 | ||
|
|
0d559b8bb6 | ||
|
|
b63ab4b190 | ||
|
|
394628eaad | ||
|
|
02f34ad351 | ||
|
|
9a567c14a6 | ||
|
|
c35c9a20f9 | ||
|
|
7676831b3b | ||
|
|
d958b56f64 | ||
|
|
f53ad7a2a1 | ||
|
|
068f799e5e | ||
|
|
123fca01f8 | ||
|
|
092ea99273 | ||
|
|
efeea7d750 | ||
|
|
ccbbd071c4 | ||
|
|
1576b650c1 | ||
|
|
3890f3f4c4 | ||
|
|
b16bff8263 | ||
|
|
9a721bfbdf | ||
|
|
b6f29a6a91 | ||
|
|
f07b5b8c06 | ||
|
|
c299b1823e | ||
|
|
58e1adf0ed | ||
|
|
21a9b20c09 | ||
|
|
9bbd0fb2e3 | ||
|
|
906664e1fd | ||
|
|
0f966e3c2c | ||
|
|
ae4b7b8bac | ||
|
|
127c583d71 | ||
|
|
5c8c212ef2 | ||
|
|
9444500b7a | ||
|
|
c88c009777 | ||
|
|
667a05f328 | ||
|
|
925012f762 | ||
|
|
bcc5f6bb2b | ||
|
|
7b214758ce | ||
|
|
e0fe0489a2 | ||
|
|
824106bd8a | ||
|
|
2d5c43b75b | ||
|
|
67a069032f | ||
|
|
10dadc29ed | ||
|
|
ed3bab7a26 | ||
|
|
e70fabfd77 | ||
|
|
8a74f7b94c | ||
|
|
3d115d9a9c | ||
|
|
a87fa8cb0f | ||
|
|
3eb1a5b27c | ||
|
|
ffecde4ae0 | ||
|
|
edb49c10cd | ||
|
|
020a628d7f | ||
|
|
8a4f4c852c | ||
|
|
9516d7c9b5 | ||
|
|
917f0aa07e | ||
|
|
86becbc492 | ||
|
|
497a4ddf3a | ||
|
|
275e5b8011 | ||
|
|
2852f3f723 | ||
|
|
bbc2b3a3b3 | ||
|
|
8f15f0242d | ||
|
|
5ef437d181 | ||
|
|
47ee9f1275 | ||
|
|
eea3555d80 | ||
|
|
7aa6e58f9d | ||
|
|
6bcdd15914 | ||
|
|
c972f10b73 | ||
|
|
1f98845adf | ||
|
|
077fe250d0 | ||
|
|
46de0243b4 | ||
|
|
d9c5333ca7 | ||
|
|
7999fce839 | ||
|
|
080d2de616 | ||
|
|
5686615382 | ||
|
|
4d4c321255 | ||
|
|
741407ecbb | ||
|
|
1701aa5acc | ||
|
|
d7df76f975 | ||
|
|
bebd3c1ddc | ||
|
|
0b6db12ab9 | ||
|
|
dfe96494cf | ||
|
|
8203bd379e |
No files matched your search
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
CI_BRANCH="$1"
|
||||
CI_JSDEC="$PWD"
|
||||
CI_RZ_VERSION=$2
|
||||
|
||||
if [ "$CI_BRANCH" != "dev" ]; then
|
||||
# stable branch always build against latest release of rizin
|
||||
CI_RZ_VERSION=$(curl -s GET https://api.github.com/repos/rizinorg/rizin/tags\?per_page\=1 | jq -r '.[].name')
|
||||
else
|
||||
CI_RZ_VERSION="$CI_BRANCH"
|
||||
fi
|
||||
|
||||
echo "CI_BRANCH: $CI_BRANCH"
|
||||
echo "CI_RZ_VERSION: $CI_RZ_VERSION"
|
||||
echo "CI_JSDEC: $CI_JSDEC"
|
||||
|
||||
# avoid placing rizin in the same folder.
|
||||
cd ..
|
||||
|
||||
# download rizin
|
||||
if [ "$CI_BRANCH" == "dev" ]; then
|
||||
# dev branch always build against latest commit of rizin
|
||||
wget -O "rizin.tar.gz" "https://github.com/rizinorg/rizin/archive/refs/heads/dev.tar.gz"
|
||||
tar xf "rizin.tar.gz"
|
||||
else
|
||||
# stable branch always build against latest release of rizin
|
||||
wget -O "rizin.tar.xz" "https://github.com/rizinorg/rizin/releases/download/$CI_RZ_VERSION/rizin-src-$CI_RZ_VERSION.tar.xz"
|
||||
tar xf "rizin.tar.xz"
|
||||
fi
|
||||
|
||||
# build rizin and install it.
|
||||
cd "rizin-$CI_RZ_VERSION"
|
||||
meson setup --buildtype=release -Denable_tests=false build
|
||||
sudo ninja -C build install
|
||||
|
||||
# cleanup
|
||||
cd ..
|
||||
rm -rf "rizin-*"
|
||||
|
||||
# go back to the source folder of jsdec
|
||||
cd "$CI_JSDEC"
|
||||
|
||||
# build jsdec and install in the rizin dir.
|
||||
meson setup --buildtype=release -Dbuild_type=rizin --prefix=~/.local build
|
||||
sudo ninja -C build install || sleep 0
|
||||
|
||||
# check if it was installed correctly and try to run it.
|
||||
HAS_JSDEC=$(rizin -qc "Lc" | grep jsdec)
|
||||
if [ -z "$HAS_JSDEC" ]; then
|
||||
echo "rizin failed to load jsdec."
|
||||
rizin -e log.level=0 -qc "Lc"
|
||||
rizin -hh
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OUTPUT=$(rizin -qc 'af ; pdd' /bin/ls)
|
||||
CHECK=$(echo -e "$OUTPUT" | grep "jsdec pseudo code output")
|
||||
echo -e "$OUTPUT"
|
||||
if [ -z "$CHECK" ]; then
|
||||
echo "rizin failed to exec jsdec."
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,33 @@
|
||||
.ci-scripts\vsdevenv.ps1 64
|
||||
|
||||
function Invoke-NativeCommand() {
|
||||
if ($args.Count -eq 0) {
|
||||
throw "Must supply some arguments."
|
||||
}
|
||||
|
||||
$command = $args[0]
|
||||
$commandArgs = @()
|
||||
if ($args.Count -gt 1) {
|
||||
$commandArgs = $args[1..($args.Count - 1)]
|
||||
}
|
||||
|
||||
& $command $commandArgs
|
||||
$result = $LASTEXITCODE
|
||||
|
||||
if ($result -ne 0) {
|
||||
throw "$command $commandArgs exited with code $result."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$rizin_path = "C:$env:HOMEPATH\AppData\Local\Programs\rizin"
|
||||
$env:PATH = "$env:PATH;C:$env:HOMEPATH\AppData\Local\Programs\rizin\bin"
|
||||
$env:PKG_CONFIG_PATH = "C:$env:HOMEPATH\AppData\Local\Programs\rizin\lib\pkgconfig"
|
||||
$env:CFLAGS = "-IC:$env:HOMEPATH\AppData\Local\Programs\rizin\include\librz -IC:$env:HOMEPATH\AppData\Local\Programs\rizin\include\librz\sdb"
|
||||
$env:LDFLAGS = "-LC:$env:HOMEPATH\AppData\Local\Programs\rizin\lib"
|
||||
|
||||
|
||||
Invoke-NativeCommand meson setup --buildtype=release --prefix="$rizin_path" build
|
||||
Invoke-NativeCommand ninja -C build install
|
||||
rizin.exe -e log.level=2 -qc "Lc"
|
||||
rizin.exe -qc "af ; pdd" "C:\Windows\System32\calc.exe"
|
||||
@@ -2,7 +2,7 @@
|
||||
set -e
|
||||
|
||||
## NPM eslint
|
||||
npm install -s eslint
|
||||
npm install --save-dev eslint
|
||||
|
||||
## NPM test
|
||||
find ./libdec -type f -name "*.js" | xargs node_modules/.bin/eslint -c ./.eslintrc.json
|
||||
find ./js -type f -name "*.js" | xargs node_modules/.bin/eslint --stats -c ./.eslint.config.js
|
||||
@@ -0,0 +1,24 @@
|
||||
import urllib.request
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
out_file = "rizin.zip"
|
||||
|
||||
latest = "stable" if len(sys.argv) < 1 else sys.argv[1]
|
||||
|
||||
if latest != "dev":
|
||||
# stable branch always build against latest release of rizin
|
||||
tags = None
|
||||
with urllib.request.urlopen('https://api.github.com/repos/rizinorg/rizin/tags?per_page=1') as f:
|
||||
tags = json.load(f)
|
||||
latest = tags[0]['name']
|
||||
url = f"https://github.com/rizinorg/rizin/archive/refs/tags/{latest}.zip"
|
||||
else:
|
||||
# dev branch always build against latest commit of rizin
|
||||
url = "https://github.com/rizinorg/rizin/archive/refs/heads/dev.zip"
|
||||
|
||||
print(f"Using rizin branch: {latest}")
|
||||
print(f"{url} as {out_file}")
|
||||
|
||||
urllib.request.urlretrieve(url, out_file)
|
||||
+10
-17
@@ -1,24 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
sudo apt update > /dev/null
|
||||
sudo apt -y install meson ninja-build
|
||||
|
||||
CI_BRANCH="$1"
|
||||
CI_JSDEC="$PWD"
|
||||
|
||||
echo "Branch: $CI_BRANCH"
|
||||
make --no-print-directory testbin -C p
|
||||
ERRORED=$?
|
||||
if [ "$ERRORED" == "1" ]; then
|
||||
exit $ERRORED
|
||||
fi
|
||||
echo "CI_BRANCH: $CI_BRANCH"
|
||||
echo "CI_JSDEC: $CI_JSDEC"
|
||||
|
||||
## jsdec-test
|
||||
cd ..
|
||||
CI_WORKDIR=$(pwd)
|
||||
echo "Work dir: $CI_WORKDIR"
|
||||
|
||||
rm -rf jsdec-test >/dev/null 2>&1 || echo "no need to clean.."
|
||||
git clone --branch "$CI_BRANCH" --depth 1 https://github.com/rizinorg/jsdec-test || git clone --depth 1 https://github.com/rizinorg/jsdec-test
|
||||
cd jsdec-test
|
||||
rm -rf tests >/dev/null 2>&1 || echo "no need to clean.."
|
||||
git clone --branch "$CI_BRANCH" --depth 1 https://github.com/rizinorg/jsdec-test tests || git clone --depth 1 https://github.com/rizinorg/jsdec-test tests
|
||||
cd tests
|
||||
chmod +x testall.sh
|
||||
./testall.sh "$CI_WORKDIR/jsdec" travis
|
||||
ERRORED=$?
|
||||
cd ..
|
||||
./testall.sh "$CI_JSDEC" travis
|
||||
@@ -1,7 +1,10 @@
|
||||
$bits = $args[0]
|
||||
$installationPath = vswhere.exe -latest -property installationPath
|
||||
if ($installationPath -and (test-path "$installationPath\Common7\Tools\vsdevcmd.bat")) {
|
||||
& "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -no_logo && set" | foreach-object {
|
||||
$name, $value = $_ -split '=', 2
|
||||
set-content env:\"$name" $value
|
||||
if (-not $installationPath -or -not (test-path "$installationPath\VC\Auxiliary\Build\vcvars$bits.bat")) {
|
||||
throw "vcvars$bits.bat file not found"
|
||||
}
|
||||
& "${env:COMSPEC}" /s /c "`"$installationPath\VC\Auxiliary\Build\vcvars$bits.bat`" > nul 2>&1 && set" | . { process {
|
||||
if ($_ -match '^([^=]+)=(.*)') {
|
||||
[System.Environment]::SetEnvironmentVariable($matches[1], $matches[2])
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -0,0 +1,26 @@
|
||||
Language: Cpp
|
||||
MaxEmptyLinesToKeep: 1
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInContainerLiterals: true
|
||||
BasedOnStyle: LLVM
|
||||
ContinuationIndentWidth: 8
|
||||
IndentCaseLabels: false
|
||||
IndentFunctionDeclarationAfterType: false
|
||||
IndentWidth: 8
|
||||
UseTab: ForContinuationAndIndentation
|
||||
ColumnLimit: 0
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AlignConsecutiveMacros: true
|
||||
AlignTrailingComments: false
|
||||
AlignOperands: false
|
||||
Cpp11BracedListStyle: false
|
||||
ForEachMacros: ['rz_list_foreach', 'rz_list_foreach_safe', 'rz_pvector_foreach', 'rz_rbtree_foreach', 'rz_interval_tree_foreach', 'ls_foreach', 'rz_skiplist_foreach', 'graph_foreach_anode']
|
||||
SortIncludes: false
|
||||
@@ -1,10 +1,22 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "script",
|
||||
"ecmaFeatures": {}
|
||||
module.exports = {
|
||||
"languageOptions": {
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {}
|
||||
},
|
||||
"globals": {
|
||||
"process": true,
|
||||
"BigInt": true,
|
||||
"Global": true,
|
||||
"Limits": true,
|
||||
"rizin": true,
|
||||
"atob": true,
|
||||
"btoa": true,
|
||||
"unit": true,
|
||||
"console": true
|
||||
}
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"semi": [2, "always"],
|
||||
"no-console": ["error", {
|
||||
@@ -18,6 +30,7 @@
|
||||
}],
|
||||
"no-unused-vars": ["error", {
|
||||
"varsIgnorePattern": "jsdec_|\\binclude\\b",
|
||||
"caughtErrors": "none",
|
||||
"args": "none"
|
||||
}],
|
||||
"curly": "error",
|
||||
@@ -26,19 +39,6 @@
|
||||
"no-constant-condition": ["error", {
|
||||
"checkLoops": false
|
||||
}],
|
||||
"no-control-regex": "warn"
|
||||
},
|
||||
"globals": {
|
||||
"Global": true,
|
||||
"Duktape": true,
|
||||
"TextDecoder": true,
|
||||
"require": true,
|
||||
"include": true,
|
||||
"read_file": true,
|
||||
"module": true,
|
||||
"console": true,
|
||||
"rzcmd": true,
|
||||
"___internal_load": true,
|
||||
"___internal_require": true
|
||||
"no-control-regex": "warn"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
@@ -0,0 +1,66 @@
|
||||
name: build-plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- stable
|
||||
- dev
|
||||
pull_request:
|
||||
|
||||
# Automatically cancel any previous workflow on new push.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout commit
|
||||
uses: actions/checkout@v6
|
||||
- name: Install dependencies
|
||||
run: sudo apt -y install meson ninja-build
|
||||
- name: Build & run jsdec as rizin plugin
|
||||
run: bash .ci-scripts/ci-build-linux.sh "${{ github.event.pull_request.base.ref || github.ref_name }}"
|
||||
|
||||
windows-latest:
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- "windows-latest"
|
||||
- "windows-2022"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.9.x
|
||||
- name: Preparing msvc toolchain
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: x64
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
pip install ninja meson PyYAML
|
||||
choco install pkgconfiglite
|
||||
choco install zip
|
||||
- name: Fetch & build rizin
|
||||
shell: bash
|
||||
run: |
|
||||
WORKDIR="$PWD"
|
||||
cd ..
|
||||
python "$WORKDIR/.ci-scripts/ci-rizin-dl.py" '${{ github.event.pull_request.base.ref || github.ref_name }}'
|
||||
unzip -q rizin.zip
|
||||
rm *.zip
|
||||
mv rizin* rizin-build
|
||||
cd rizin-build
|
||||
powershell.exe ".\dist\windows\build_windows_installer.ps1 vs2019_static 64 --default-library=shared -Dportable=true"
|
||||
ls ./dist/windows/Output
|
||||
powershell.exe '.\dist\windows\Output\rizin.exe /SP- /SILENT /CURRENTUSER'
|
||||
cd "$WORKDIR"
|
||||
- name: Build & run jsdec as rizin plugin
|
||||
shell: pwsh
|
||||
run: .ci-scripts\ci-build-win.ps1 64
|
||||
@@ -0,0 +1,41 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["stable", "dev"]
|
||||
pull_request:
|
||||
branches: ["stable", "dev"]
|
||||
schedule:
|
||||
- cron: "1 14 * * 5"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [javascript]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
@@ -1,24 +1,37 @@
|
||||
name: continuous-tests
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- stable
|
||||
- dev
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
eslint:
|
||||
name: eslint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: "21"
|
||||
- name: eslint
|
||||
run: chmod +x .ci-scripts/ci-eslint.sh && .ci-scripts/ci-eslint.sh ${GITHUB_REF##*/}
|
||||
run: chmod +x .ci-scripts/ci-eslint.sh && .ci-scripts/ci-eslint.sh
|
||||
|
||||
tests:
|
||||
name: tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout jsdec-test
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v6
|
||||
- name: tests
|
||||
run: chmod +x .ci-scripts/ci-tests.sh && .ci-scripts/ci-tests.sh ${GITHUB_REF##*/}
|
||||
run: chmod +x .ci-scripts/ci-tests.sh && .ci-scripts/ci-tests.sh "${{ github.ref_name }}"
|
||||
|
||||
licenses:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
- name: Reuse Compliance Check
|
||||
uses: fsfe/reuse-action@v6
|
||||
with:
|
||||
args: --suppress-deprecation lint
|
||||
@@ -1,132 +0,0 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'release-*'
|
||||
|
||||
jobs:
|
||||
|
||||
#build-osx:
|
||||
# name: Macos build
|
||||
# runs-on: macos-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: build
|
||||
# run: make ./p/
|
||||
|
||||
#build-windows:
|
||||
# name: Windows build
|
||||
# runs-on: windows-latest
|
||||
# strategy:
|
||||
# fail-fast: false
|
||||
# matrix:
|
||||
# name: [vs2019_static, clang_cl]
|
||||
# include:
|
||||
# - name: vs2019_static
|
||||
# compiler: cl
|
||||
# meson_options: --default-library=static -Dstatic_runtime=true
|
||||
# - name: clang_cl
|
||||
# compiler: clang-cl
|
||||
# meson_options: --default-library=shared
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: actions/setup-python@v2
|
||||
# - name: install dependencies
|
||||
# shell: pwsh
|
||||
# run: |
|
||||
# python -m pip install --upgrade pip
|
||||
# pip install meson ninja
|
||||
# - name: build with meson + ninja
|
||||
# shell: pwsh
|
||||
# run: |
|
||||
# .github\vsdevenv.ps1
|
||||
# meson --buildtype=release --prefix=$PWD\rizin-install build ${{ matrix.meson_options }}
|
||||
# ninja -C build
|
||||
|
||||
build-deb:
|
||||
name: build deb package for ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.container }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
name: [debian-buster, ubuntu-1804]
|
||||
include:
|
||||
- name: debian-buster
|
||||
container: debian:buster
|
||||
- name: ubuntu-1804
|
||||
container: ubuntu:18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: extract rizin version
|
||||
shell: bash
|
||||
run: echo "##[set-output name=version;]$(echo ${GITHUB_REF##*/} | sed 's/release-//')"
|
||||
id: rizin
|
||||
- name: install requirements
|
||||
run: |
|
||||
apt-get update -qq > /dev/null
|
||||
apt-get install --yes sudo wget pkg-config build-essential libuv1-dev libssl-dev libc6 -qq > /dev/null
|
||||
- name: install rizin and rizin-dev
|
||||
run: |
|
||||
export VERSION=${{ steps.rizin.outputs.version }}
|
||||
export DISTRIB=${{ matrix.name }}
|
||||
export GH_URL="https://github.com/rizinorg/rizin/releases/download/$VERSION"
|
||||
wget --quiet -c "$GH_URL/rizin-""$DISTRIB""_""$VERSION""_amd64.deb"
|
||||
wget --quiet -c "$GH_URL/rizin-dev-""$DISTRIB""_""$VERSION""_amd64.deb"
|
||||
sudo dpkg -i *.deb
|
||||
rm -rf *.deb || sleep 0
|
||||
- name: build
|
||||
run: |
|
||||
make -C p/dist/debian
|
||||
mv p/dist/debian/*.deb p/dist/debian/jsdec-${{ matrix.name }}_${{ steps.rizin.outputs.version }}_amd64.deb
|
||||
- name: upload .deb file
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: jsdec-${{ matrix.name }}-${{ steps.rizin.outputs.version }}-deb
|
||||
path: p/dist/debian/jsdec-${{ matrix.name }}_${{ steps.rizin.outputs.version }}_amd64.deb
|
||||
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-deb]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: extract rizin version
|
||||
shell: bash
|
||||
run: echo "##[set-output name=version;]$(echo ${GITHUB_REF##*/} | sed 's/release-//')"
|
||||
id: rizin
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
- name: display structure of downloaded files
|
||||
run: ls -R
|
||||
|
||||
- name: create release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.rizin.outputs.version }}
|
||||
release_name: Release ${{ steps.rizin.outputs.version }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
- name: upload debian:buster package
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./jsdec-debian-buster-${{ steps.rizin.outputs.version }}-deb/jsdec-debian-buster_${{ steps.rizin.outputs.version }}_amd64.deb
|
||||
asset_name: jsdec-debian-buster_${{ steps.rizin.outputs.version }}_amd64.deb
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: upload ubuntu:18.04 package
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./jsdec-ubuntu-1804-${{ steps.rizin.outputs.version }}-deb/jsdec-ubuntu-1804_${{ steps.rizin.outputs.version }}_amd64.deb
|
||||
asset_name: jsdec-ubuntu-1804_${{ steps.rizin.outputs.version }}_amd64.deb
|
||||
asset_content_type: application/zip
|
||||
+5
-10
@@ -1,10 +1,5 @@
|
||||
/nbproject/
|
||||
/node_modules/
|
||||
p/build
|
||||
p/clean
|
||||
p/core_pdd.so
|
||||
p/jsdec-test
|
||||
/themes/*.json
|
||||
core_pdd.so
|
||||
p/*.o
|
||||
p/duktape/*.o
|
||||
build*/
|
||||
subprojects/libquick*/
|
||||
node_modules/
|
||||
package.json
|
||||
package-lock.json
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: JSDec
|
||||
Upstream-Contact: Giovanni Dante Grazioli <[email protected]>
|
||||
Source: https://github.com/rizinorg/jsdec
|
||||
|
||||
Files: .ci-scripts/*
|
||||
Copyright: 2020 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: .github/*
|
||||
Copyright: 2018-2021 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: .github/**/*
|
||||
Copyright: 2018-2021 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: .eslint.config.js
|
||||
Copyright: 2018-2021 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: .gitignore
|
||||
Copyright: 2017-2021 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: *.md
|
||||
Copyright: 2017-2021 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: eslint-check.sh
|
||||
Copyright: 2018-2019 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: subprojects/*
|
||||
Copyright: 2023 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: subprojects/**/*
|
||||
Copyright: 2023 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: meson_options.txt
|
||||
Copyright: 2021 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: .clang-format
|
||||
Copyright: 2021 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: cutter-plugin/CMakeLists.txt
|
||||
Copyright: 2024 Giovanni Dante Grazioli <[email protected]>
|
||||
License: BSD-3-Clause
|
||||
+47
-48
@@ -1,54 +1,50 @@
|
||||
# Developing on jsdec
|
||||
|
||||
jsdec is mostly written in javascript and the engine is duktape.
|
||||
|
||||
Duktape APIs: https://duktape.org/api.html
|
||||
`jsdec` is written in javascript uses [QuickJS](https://bellard.org/quickjs/).
|
||||
|
||||
## Extending jsdec arch
|
||||
|
||||
First of all when you need to add a new architecture, you need to create a new `.js` file inside `libdec/arch/`.
|
||||
First of all when you need to add a new architecture, you need to create a new `.js` file inside `js/libdec/arch/`.
|
||||
|
||||
For example `libdec/arch/arch9999.js` and it needs to follow the minimal base javascript template:
|
||||
For example `js/libdec/arch/arch9999.js` and it needs to follow the minimal base javascript template:
|
||||
```js
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
const Base = require('libdec/core/base');
|
||||
const Variable = require('libdec/core/variable');
|
||||
const Extra = require('libdec/core/extra');
|
||||
return {
|
||||
instructions: {
|
||||
add: function(instr, context, instructions) {
|
||||
return Base.add(instr.parsed.opd[0], instr.parsed.opd[1], instr.parsed.opd[2]);
|
||||
},
|
||||
nop: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
invalid: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
import Extra from '../core/extra.js';
|
||||
return {
|
||||
instructions: {
|
||||
add: function(instr, context, instructions) {
|
||||
return Base.add(instr.parsed.opd[0], instr.parsed.opd[1], instr.parsed.opd[2]);
|
||||
},
|
||||
parse: function(assembly) {
|
||||
var tokens = assembly.trim().split(' ');
|
||||
return { mnem: tokens.shift(), opd: tokens };
|
||||
nop: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
context: function() {
|
||||
return { cond: { a: '?', b: '?' } };
|
||||
},
|
||||
preanalisys: function(instructions, context) {},
|
||||
postanalisys: function(instructions, context) {},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
invalid: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
parse: function(assembly) {
|
||||
var tokens = assembly.trim().split(' ');
|
||||
return { mnem: tokens.shift(), opd: tokens };
|
||||
},
|
||||
context: function() {
|
||||
return { cond: { a: '?', b: '?' } };
|
||||
},
|
||||
preanalisys: function(instructions, context) {},
|
||||
postanalisys: function(instructions, context) {},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
```
|
||||
After saving the new arch (`arch9999.js` in the example), you need to add this arch to the file `libdec/Archs.js`.
|
||||
|
||||
@@ -56,13 +52,16 @@ The new architecture needs to have the same name as the cmd `e asm.arch`, becaus
|
||||
|
||||
For example:
|
||||
```js
|
||||
(function() {
|
||||
return {
|
||||
arm: require('libdec/arch/arm'),
|
||||
arch9999: require('libdec/arch/arch9999'),
|
||||
x86: require('libdec/arch/x86')
|
||||
};
|
||||
});
|
||||
|
||||
import arm from './arch/arm.js';
|
||||
import x86 from './arch/x86.js';
|
||||
import arch9999 from './arch/arch9999.js';
|
||||
|
||||
export default {
|
||||
arm: arm,
|
||||
arch9999: arch9999,
|
||||
x86: x86
|
||||
};
|
||||
```
|
||||
|
||||
## Codebase:
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
Copyright (c) <year> <owner>. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,9 +1,6 @@
|
||||

|
||||
[](https://www.codefactor.io/repository/github/rizinorg/jsdec)
|
||||
[](https://lgtm.com/projects/g/rizinorg/jsdec/context:javascript)
|
||||
[](https://lgtm.com/projects/g/rizinorg/jsdec/alerts/)
|
||||
[](https://opensource.org/licenses/BSD-3-Clause)
|
||||
|
||||

|
||||

|
||||
|
||||
Converts asm to pseudo-C code.
|
||||
|
||||
@@ -11,23 +8,19 @@ Converts asm to pseudo-C code.
|
||||
|
||||
Requires [rizin](https://github.com/rizinorg/rizin).
|
||||
|
||||
# Install
|
||||
# Install in home folder (or in other paths)
|
||||
|
||||
Follow the following steps to install jsdec via rzpm
|
||||
If you want to install in the HOME folder or in other non standard paths, just
|
||||
set the `prefix` to a different value
|
||||
- Run ``meson setup build --prefix=~/.local``
|
||||
- Run `ninja -C build install`
|
||||
|
||||
### *nix users (Linux/OSX/etc..):
|
||||
# Install system wide
|
||||
|
||||
rz-pm init
|
||||
rz-pm install jsdec
|
||||
|
||||
### Windows users only:
|
||||
|
||||
- clone
|
||||
- For cygwin or msys2
|
||||
- run `make -C p` from inside the `jsdec` folder
|
||||
- For everything else
|
||||
- Build via meson.
|
||||
- Set `JSDEC_HOME` env variable or copy whole jsdec dir to `%RZ_RDATAHOME%\rzpm\git\` where `%RZ_RDATAHOME%` is path returned by `rizin.exe -H RZ_RDATAHOME`.
|
||||
Follow the following steps to install jsdec
|
||||
- clone this repository
|
||||
- Run `meson setup build --prefix=/usr` to create the build folder
|
||||
- Run `ninja -C build install` to build the shared library and to install it
|
||||
|
||||
# Usage
|
||||
|
||||
@@ -38,34 +31,18 @@ Follow the following steps to install jsdec via rzpm
|
||||
# Arguments
|
||||
|
||||
```
|
||||
[0x00000000]> pdd?Usage: pdd [args] - core plugin for jsdec
|
||||
pdd - decompile current function
|
||||
pdd? - show this help
|
||||
pdd* - the decompiled code is returned to rizin as comment (via CCu)
|
||||
pdda - decompile current function side by side with assembly
|
||||
pddb - decompile current function but shows only scopes
|
||||
pddo - decompile current function side by side with offsets
|
||||
pddi - generates the issue data
|
||||
|
||||
Environment
|
||||
JSDEC_HOME defaults to the root directory of the jsdec repo
|
||||
|
||||
[0x00000000]> pdd --help
|
||||
|
||||
jsdec [options]
|
||||
--help | this help message
|
||||
--assembly | shows pseudo next to the assembly
|
||||
--blocks | shows only scopes blocks
|
||||
--casts | shows all casts in the pseudo code
|
||||
--colors | enables syntax colors
|
||||
--debug | do not catch exceptions
|
||||
--html | outputs html data instead of text
|
||||
--issue | generates the json used for the test suite
|
||||
--offsets | shows pseudo next to the assembly offset
|
||||
--paddr | all xrefs uses physical addresses instead of virtual addresses
|
||||
--xrefs | shows also instruction xrefs in the pseudo code
|
||||
--as-comment | the decompiled code is returned to rizin as comment (via CCu)
|
||||
--as-opcode | the decompiled code is returned to rizin as opcode (via aho)
|
||||
[0x00000000]> pdd?
|
||||
Usage: pdd[?] # Core plugin for jsdec
|
||||
| pdd # decompile current function
|
||||
| pddt # lists the supported architectures
|
||||
| pdda # decompile current function with side assembly
|
||||
| pddA # decompile current function with annotation output
|
||||
| pddb # decompile current function but show only scopes
|
||||
| pddc # decompiled code is returned to rizin as 'file:line code' (via CL)
|
||||
| pddf # decompile all functions
|
||||
| pddi # generate issue data
|
||||
| pddj # decompile current function as json
|
||||
| pddo # decompile current function side by side with offsets
|
||||
```
|
||||
|
||||
# Evaluable vars
|
||||
@@ -79,7 +56,6 @@ jsdec.casts | if false, hides all casts in the pseudo code.
|
||||
jsdec.debug | do not catch exceptions in jsdec.
|
||||
jsdec.paddr | if true, all xrefs uses physical addresses compare.
|
||||
jsdec.slow | if true load all the data before to avoid multirequests to rizin.
|
||||
jsdec.theme | defines the color theme to be used on jsdec.
|
||||
jsdec.xrefs | if true, shows all xrefs in the pseudo code.
|
||||
e scr.html | outputs html data instead of text.
|
||||
e scr.color | enables syntax colors.
|
||||
@@ -95,93 +71,21 @@ e scr.color | enables syntax colors.
|
||||
# Supported Arch
|
||||
|
||||
6502 (experimental)
|
||||
arm 16/32/64 bit
|
||||
avr
|
||||
8051
|
||||
ARM 16/32/64 bit
|
||||
AVR
|
||||
dalvik
|
||||
m68k (experimental)
|
||||
mips
|
||||
ppc 32/64 bit (VLE included)
|
||||
superh (experimental)
|
||||
sparc
|
||||
m68k
|
||||
MIPS
|
||||
PowerPC 32/64 bit (VLE included)
|
||||
RISC-V
|
||||
Sparc
|
||||
SuperH (experimental)
|
||||
v850
|
||||
wasm (experimental)
|
||||
WebAssembly (experimental)
|
||||
x86/x64
|
||||
|
||||
# Developing on jsdec
|
||||
|
||||
[Read DEVELOPERS.md](https://github.com/rizinorg/jsdec/blob/master/DEVELOPERS.md)
|
||||
[Read DEVELOPERS.md](https://github.com/rizinorg/jsdec/blob/dev/DEVELOPERS.md)
|
||||
|
||||
## Example
|
||||
|
||||
This example shows a possible dump of the plugin.
|
||||
|
||||
### Source Code
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char const *argv[]) {
|
||||
int var = 0;
|
||||
while(var < 0x90) {
|
||||
if(var < 0x10) {
|
||||
var += 0x50;
|
||||
}
|
||||
var += 0x10;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### rizin view
|
||||
|
||||
|
||||
```
|
||||
╭ (fcn) main 50
|
||||
│ main (int arg1, int arg2);
|
||||
│ ; var int local_20h @ rbp-0x20
|
||||
│ ; var int local_14h @ rbp-0x14
|
||||
│ ; var signed int local_4h @ rbp-0x4
|
||||
│ ; DATA XREF from entry0 (0x1041)
|
||||
│ 0x00001119 55 push rbp
|
||||
│ 0x0000111a 4889e5 mov rbp, rsp
|
||||
│ 0x0000111d 897dec mov dword [local_14h], edi ; arg1
|
||||
│ 0x00001120 488975e0 mov qword [local_20h], rsi ; arg2
|
||||
│ 0x00001124 c745fc000000. mov dword [local_4h], 0
|
||||
│ ╭─< 0x0000112b eb0e jmp 0x113b
|
||||
│ │ ; CODE XREF from main (0x1142)
|
||||
│ ╭──> 0x0000112d 837dfc0f cmp dword [local_4h], 0xf ; [0xf:4]=0x3e000300
|
||||
│ ╭───< 0x00001131 7f04 jg 0x1137
|
||||
│ │⋮│ 0x00001133 8345fc50 add dword [local_4h], 0x50 ; 'P'
|
||||
│ │⋮│ ; CODE XREF from main (0x1131)
|
||||
│ ╰───> 0x00001137 8345fc10 add dword [local_4h], 0x10
|
||||
│ ⋮│ ; CODE XREF from main (0x112b)
|
||||
│ ⋮╰─> 0x0000113b 817dfc8f0000. cmp dword [local_4h], 0x8f ; [0x8f:4]=0x2a800
|
||||
│ ╰──< 0x00001142 7ee9 jle 0x112d
|
||||
│ 0x00001144 b800000000 mov eax, 0
|
||||
│ 0x00001149 5d pop rbp
|
||||
╰ 0x0000114a c3 ret
|
||||
```
|
||||
|
||||
### jsdec pseudo-C code
|
||||
|
||||
```c
|
||||
/* jsdec pseudo C output */
|
||||
#include <stdint.h>
|
||||
|
||||
int32_t main (int32_t argc, char ** argv) {
|
||||
char ** local_20h;
|
||||
int32_t local_14h;
|
||||
int32_t local_4h;
|
||||
local_14h = edi;
|
||||
local_20h = rsi;
|
||||
local_4h = 0;
|
||||
while (local_4h <= 0x8f) {
|
||||
if (local_4h <= 0xf) {
|
||||
local_4h += 0x50;
|
||||
}
|
||||
local_4h += 0x10;
|
||||
}
|
||||
eax = 0;
|
||||
return eax;
|
||||
}
|
||||
```
|
||||
+403
@@ -0,0 +1,403 @@
|
||||
// SPDX-FileCopyrightText: 2013 William Sherif
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
// MAPS
|
||||
// b64 maps 0=>A, 1=>B..63=>/ etc
|
||||
// ----------1---------2---------3---------4---------5---------6---
|
||||
// 0123456789012345678901234567890123456789012345678901234567890123
|
||||
const static char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
// unb64 maps A=>0, B=>1.. and all others not present in base64 alphabet to 0.
|
||||
// You can clearly see here why base64 encoding is a really bloated representation
|
||||
// of the original data: look how many entries are unused. Each ascii character
|
||||
// can index any value between 0-255 in an array, but we're only using 64 of
|
||||
// the available slots for meaningful values, leaving 192/256 values unused.
|
||||
// clang-format off
|
||||
const static unsigned char unb64[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //10
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40
|
||||
0, 0, 0, 62, 0, 0, 0, 63, 52, 53, //50
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 0, 0, //60
|
||||
0, 0, 0, 0, 0, 0, 1, 2, 3, 4, //70
|
||||
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, //80
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, //90
|
||||
25, 0, 0, 0, 0, 0, 0, 26, 27, 28, //100
|
||||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, //110
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, //120
|
||||
49, 50, 51, 0, 0, 0, 0, 0, 0, 0, //130
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //250
|
||||
0, 0, 0, 0, 0, 0,
|
||||
}; // This array has 256 elements
|
||||
// clang-format on
|
||||
|
||||
// Converts any binary data to base64 characters.
|
||||
// Length of the resultant string is stored in flen
|
||||
// (you must pass pointer flen). flen is strlen, it doesn't
|
||||
// include the null terminator.
|
||||
// A common trip-up is not passing your null terminator into
|
||||
// this function, if you want your C string to be fully encoded,
|
||||
// you have to pass strlen(str)+1 to as binaryData as I have in the
|
||||
// examples.
|
||||
char *base64(const void *binaryData, int len, int *flen) {
|
||||
printf("Base64 encoding %d bytes of binary data\n", len);
|
||||
|
||||
// I look at your data like the stream of unsigned bytes that it is
|
||||
const unsigned char *bin = (const unsigned char *)binaryData;
|
||||
|
||||
int lenMod3 = len % 3;
|
||||
|
||||
// PAD. Base64 is all about breaking the input into SEXTETS, or 6-bit inputs.
|
||||
// If you have 1 byte of input, that's 8 bits, not 6. So it won't fit.
|
||||
// We need the input to be a multiple of 6. So 8 bits would be padded
|
||||
// by 2 bytes to make the total input size 24 bits, which is divisible by 6.
|
||||
// A 2 byte input is 16 bits, which is not divisible by 6. So we pad it
|
||||
// by 1 byte to make it 24 bits, which is now divisible by 6.
|
||||
|
||||
// We use modulus 3 bytes above because that's 24 bits, and 24 bits is
|
||||
// the lowest number that is both divisible by 6 and 8. We need the final
|
||||
// output data is to both be divisible by 6 and 8.
|
||||
int pad = ((lenMod3 & 1) << 1) + ((lenMod3 & 2) >> 1); // 2 gives 1 and 1 gives 2, but 0 gives 0.
|
||||
|
||||
*flen = 4 * (len + pad) / 3; // (len+pad) IS divisible by 3
|
||||
// So, final length IS a multiple of 4 for a valid base64 string.
|
||||
printf("%d %% 3 = %d, %d bytes pad, +1 byte NULL, flen=%d\n", len, lenMod3, pad, *flen);
|
||||
|
||||
// Allocate enough space for the base64 string result.
|
||||
char *base64String = (char *)malloc(*flen + 1); // and one for the null,
|
||||
// which is NOT counted in flen.
|
||||
if (!base64String) {
|
||||
puts("ERROR: base64 could not allocate enough memory.");
|
||||
puts("I must stop because I could not get enough");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// EXTRACTING SEXTETS FROM THE OCTETS.
|
||||
// byte0 byte1 byte2
|
||||
// +-----------+-----------+-----------+
|
||||
// | 0000 0011 0111 1011 1010 1101 |
|
||||
// +-AAAA AABB BBBB CCCC CCDD DDDD
|
||||
//
|
||||
// In 3 bytes (really, 3 "octets") there are __4__ sextets.
|
||||
// You can see that from the diagram above. byte0 (0000 0011) contains
|
||||
// the first sextet (AAAA AA) and 2 bits of the 2nd sextet (BB).
|
||||
// byte1 contains the next 4 bits of the 2nd sextet (BBBB) and 4 bits
|
||||
// of the 3rd sextet (CCCC). byte2 has 2 bits of the 3rd sextet and
|
||||
// all 6 bits of the 4th sextet.
|
||||
//
|
||||
// You can see why we process in groups of 3 bytes: because 3*8 = 24
|
||||
// and 24 is the lowest common multiple between 6 and 8. To divide
|
||||
// a group of bytes EVENLY into groups of 6, the number of bytes has to
|
||||
// be a multiple of 3.
|
||||
|
||||
// Talking in bits, the input already HAS to be a multiple of 8 (because you just
|
||||
// can't have anything smaller than a byte saved to memory or disk on modern
|
||||
// computers). To successfully convert the bitstream into groups of 6 bits, we'll force
|
||||
// the input bitstream to being a MULTIPLE OF 24, so that it will evenly
|
||||
// divide by 6.
|
||||
|
||||
// For that reason, we have the concept of PADDING: if the original octet
|
||||
// stream is NOT a multiple of 3, then we pad it with 1 or 2 extra bytes
|
||||
// so that it is a multiple of 3.
|
||||
|
||||
// So without further ado let's extract the 4 sextets from the 3 octets!
|
||||
// Convert sextets in stream into the base64 alphabet using b64 array
|
||||
// the value in 6 bits can never be larger than 63, but the b64 array
|
||||
// protects us from OOB accesses anyway by providing
|
||||
|
||||
// We want to shift the first 6 bits in the above diagram down to sitting
|
||||
// flushed to the right. So we want bin[0] (containing AAAA AABB) to just
|
||||
// become 00AA AAAA. We do that with a shift right of 2 bits.
|
||||
|
||||
// We take the number that comes out of that and immediately convert it to
|
||||
// the base64 character for that number by doing a direct lookup into the
|
||||
// b64 encoding array.
|
||||
|
||||
// We devise 4 formulae below, SEXTET1, SEXTET2, SEXTET3 and SEXTET4. They
|
||||
// are used to extract the 4 sextets from the 3 octets that we have.
|
||||
#define SEXTET_A(byte0) (byte0 >> 2)
|
||||
// Note that no mask needed since BYTE0 is unsigned, so 0's always come in from left
|
||||
// (even though there is implicit int promotion on R&L sides prior to actual bitshift).
|
||||
|
||||
// the second sextet BBBBBB is part of the first byte and partly in the 2nd byte.
|
||||
// BYTE0 BYTE1
|
||||
// AAAA AABB BBBB CCCC
|
||||
// The first part takes the lower 2 bits of the first byte and pushes them
|
||||
// LEFT 4: (AAAA AABB becomes 00BB 0000), then bitwise ORs to it the top 4 bits of
|
||||
// BYTE1, shifted RIGHT 4 (BBBB CCCC becomes 0000 BBBB).
|
||||
#define SEXTET_B(byte0, byte1) (((0x3 & byte0) << 4) | (byte1 >> 4))
|
||||
|
||||
// 3rd sextet CCCCCC is lower nibble of 2nd byte and upper half nibble of 3rd byte.
|
||||
// BYTE1 BYTE2
|
||||
// BBBB CCCC CCDD DDDD
|
||||
// From BYTE1, we need to get rid of the BBBB in the front, so we mask
|
||||
// those off with 0xf (0000 1111). Then we shift BYTE1 LEFT 2
|
||||
// (BBBB CCCC becomes 00CC CC00).
|
||||
// We need to fill in the bottom 2 bits of 00CC CC00 with the top 2 bits
|
||||
// in BYTE2. So we just shift BYTE2 right by 6 bits (CCDD DDDD becomes 0000 00CC).
|
||||
#define SEXTET_C(byte1, byte2) (((0xf & byte1) << 2) | (byte2 >> 6))
|
||||
|
||||
// 4th sextet
|
||||
// already low order, just mask off 2 hiorder bits
|
||||
// BYTE2
|
||||
// CCDD DDDD
|
||||
// We just want to mask off the top 2 bits, use mask 0011 1111 or just 0x3f
|
||||
#define SEXTET_D(byte2) (0x3f & byte2)
|
||||
|
||||
int i = 0, byteNo; // result counter, and which byte we're on of the original source data.
|
||||
// I still need these variables after the loop
|
||||
for (byteNo = 0; byteNo <= len - 3; // This loop is NOT entered for if there
|
||||
// are trailing bytes that are not a multiple of 3 bytes,
|
||||
// since we skip in 3's.
|
||||
// If there WAS padding, skip the last 3 octets and process below.
|
||||
// 0=>no, 1=>no, 2=>no, 3=>ONCE,4=>ONCE,5=>ONCE, 6=>2x..
|
||||
byteNo += 3) // jump in 3's
|
||||
{
|
||||
// Use unsigned char so shifts left will always bring in 0's
|
||||
unsigned char BYTE0 = bin[byteNo];
|
||||
unsigned char BYTE1 = bin[byteNo + 1];
|
||||
unsigned char BYTE2 = bin[byteNo + 2];
|
||||
// printf( "BYTE0=%d BYTE1=%d BYTE2=%d\n", BYTE0, BYTE1, BYTE2 ) ;
|
||||
|
||||
// To form the base64String, we make lookups with the base64 numeric
|
||||
// values into the base64 "alphabet" that is present in the b64 array.
|
||||
base64String[i++] = b64[SEXTET_A(BYTE0)];
|
||||
base64String[i++] = b64[SEXTET_B(BYTE0, BYTE1)];
|
||||
base64String[i++] = b64[SEXTET_C(BYTE1, BYTE2)];
|
||||
base64String[i++] = b64[SEXTET_D(BYTE2)];
|
||||
}
|
||||
|
||||
// The last 3 octets must be converted carefully as if len%3==1 or len%3==2 we must
|
||||
// "pretend" there are additional bits at the end.
|
||||
if (pad == 1) {
|
||||
unsigned char BYTE0 = bin[byteNo];
|
||||
unsigned char BYTE1 = bin[byteNo + 1];
|
||||
// printf( "BYTE0=%d BYTE1=%d\n", BYTE0, BYTE1 ) ;
|
||||
// When len%3==2 (2,5,8,11) (missing 1 byte).
|
||||
// - 3 sextets (C is 0 padded)
|
||||
// bin[0] bin[1] bin[2]
|
||||
// +-----------+-----------+-----------+
|
||||
// | 0000 0011 1111 1111 ~~~~ ~~~~ |
|
||||
// +-AAAA AABB BBBB CCCC XXXX XXXX
|
||||
// Here all the ~ are actually going to be considered __0__'s.
|
||||
base64String[i++] = b64[SEXTET_A(BYTE0)];
|
||||
base64String[i++] = b64[SEXTET_B(BYTE0, BYTE1)];
|
||||
|
||||
// We can't use the SEXTET3 formula because we only have 2 bytes to work
|
||||
// with. The 3rd byte (BYTE2) is actually 0 here. You could call
|
||||
// SEXTET3(BYTE1, 0), but to save some ops we just write what will actually
|
||||
// be needed here only.
|
||||
base64String[i++] = b64[(0xf & BYTE1) << 2];
|
||||
|
||||
// Last one is = to indicate there has been a padding of 1 byte.
|
||||
base64String[i++] = '=';
|
||||
} else if (pad == 2) // len%3==1 (1,4,7,10)
|
||||
{
|
||||
unsigned char BYTE0 = bin[byteNo];
|
||||
// printf( "BYTE0=%d\n", BYTE0 ) ;
|
||||
// We are missing 2 bytes. So
|
||||
// - we will only extract 2 sextets when len%3==1
|
||||
// - The 2nd sextet's 2 HI ORDER BITS, NOT LO-ORDER.
|
||||
// - are being specified by the lowest 2 bits of the 1st octet. these should be 0.
|
||||
// bin[0] bin[1] bin[2]
|
||||
// +-----------+-----------+-----------+
|
||||
// | 0000 0011 ~~~~ ~~~~ ~~~~ ~~~~ |
|
||||
// +-AAAA AABB XXXX XXXX XXXX XXXX
|
||||
base64String[i++] = b64[SEXTET_A(BYTE0)];
|
||||
base64String[i++] = b64[(0x3 & BYTE0) << 4]; // "padded" by 0's, these 2 bits are still HI ORDER BITS.
|
||||
// Last 2 are ==, to indicate there's been a 2 byte-pad
|
||||
base64String[i++] = '=';
|
||||
base64String[i++] = '=';
|
||||
}
|
||||
|
||||
base64String[i] = 0; // NULL TERMINATOR! ;)
|
||||
return base64String;
|
||||
}
|
||||
|
||||
unsigned char *unbase64(const char *ascii, int len, int *flen) {
|
||||
#ifdef SAFEBASE64
|
||||
if (!base64integrity(ascii, len))
|
||||
return 0; // NULL PTR if bad integrity.
|
||||
#endif
|
||||
|
||||
const unsigned char *safeAsciiPtr = (const unsigned char *)ascii; // internally I use
|
||||
// an unsigned char pointer, so that __the maximum value read out is 255,
|
||||
// and the value is never negative__. This is a type of
|
||||
// "if statement" enforced by the type of the pointer.
|
||||
// This eliminates a possible bounds check on your array lookups into unb64[]
|
||||
// (*(unsigned char*) having values between 0 and 255 means it will always be
|
||||
// inside the bounds of the 256 element array).
|
||||
|
||||
int pad = 0;
|
||||
if (len > 1) {
|
||||
// Count == on the end to determine how much it was padded.
|
||||
if (safeAsciiPtr[len - 1] == '=')
|
||||
++pad;
|
||||
if (safeAsciiPtr[len - 2] == '=')
|
||||
++pad;
|
||||
}
|
||||
|
||||
// You take the ascii string len and divide it by 4
|
||||
// to get the number of 3 octet groups. You then *3 to
|
||||
// get #octets total.
|
||||
// If len<4, we makes sure you get a flen of 0, because that's not even
|
||||
// a valid base64 string at all.
|
||||
*flen = 3 * (len / 4) - pad;
|
||||
if (*flen < 0)
|
||||
*flen = 0;
|
||||
unsigned char *bin = (unsigned char *)malloc(*flen);
|
||||
if (!bin) {
|
||||
puts("ERROR: unbase64 could not allocate enough memory.");
|
||||
puts("I must stop because I could not get enough");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cb = 0; // counter for bin
|
||||
int charNo; // counter for what base64 char we're currently decoding
|
||||
|
||||
// NEVER do the last group of 4 characters if either of the
|
||||
// last 2 chars were pad.
|
||||
for (charNo = 0; charNo <= len - 4 - pad; charNo += 4) {
|
||||
// Get the numbers each character represents
|
||||
// Since ascii is ONE BYTE, the worst that can happen is
|
||||
// you get a bunch of 0's back (if the base64 string contained
|
||||
// characters not in the base64 alphabet).
|
||||
// The only way unbase64 will TELL you about this though
|
||||
// is if you #define SAFEBASE64 (particularly because
|
||||
// there is a 3-4x performance hit, just for the integrity check.)
|
||||
int A = unb64[safeAsciiPtr[charNo]];
|
||||
// printf( "[%4d] %c => %d\n", charNo, ascii[charNo], A ) ;
|
||||
int B = unb64[safeAsciiPtr[charNo + 1]];
|
||||
// printf( "[%4d] %c => %d\n", charNo+1, ascii[charNo+1], B ) ;
|
||||
int C = unb64[safeAsciiPtr[charNo + 2]];
|
||||
// printf( "[%4d] %c => %d\n", charNo+2, ascii[charNo+2], C ) ;
|
||||
int D = unb64[safeAsciiPtr[charNo + 3]];
|
||||
// printf( "[%4d] %c => %d\n", charNo+3, ascii[charNo+3], D ) ;
|
||||
|
||||
// Just unmap each sextet to THE NUMBER it represents.
|
||||
// You then have to pack it in bin,
|
||||
// we go in groups of 4 sextets,
|
||||
// and pull out 3 octets per quad of sextets.
|
||||
// bin[0] bin[1] bin[2]
|
||||
// +-----------+-----------+-----------+
|
||||
// | 0000 0011 0111 1011 1010 1101 |
|
||||
// +-AAAA AABB BBBB CCCC CCDD DDDD
|
||||
// or them
|
||||
bin[cb++] = (A << 2) | (B >> 4); // OR in last 2 bits of B
|
||||
|
||||
// The 2nd byte is the bottom 4 bits of B for the upper nibble,
|
||||
// and the top 4 bits of C for the lower nibble.
|
||||
bin[cb++] = (B << 4) | (C >> 2);
|
||||
bin[cb++] = (C << 6) | (D); // shove C up to top 2 bits, or with D
|
||||
}
|
||||
|
||||
// If the length of the string were not a multiple of 4, then the string
|
||||
// was damaged and some data was lost.
|
||||
if (isMultipleOf(len, 4)) {
|
||||
if (pad == 1) {
|
||||
// 1 padding character.
|
||||
// bin[0] bin[1] bin[2]
|
||||
// +-----------+-----------+-----------+
|
||||
// | 0000 0011 1111 1111 ~~~~ ~~~~ |
|
||||
// +-AAAA AABB BBBB CCCC XXXX XXXX
|
||||
// We can pull 2 bytes out, not 3.
|
||||
// We have __3__ characters A,B and C, not 4.
|
||||
int A = unb64[safeAsciiPtr[charNo]];
|
||||
int B = unb64[safeAsciiPtr[charNo + 1]];
|
||||
int C = unb64[safeAsciiPtr[charNo + 2]];
|
||||
|
||||
bin[cb++] = (A << 2) | (B >> 4);
|
||||
bin[cb++] = (B << 4) | (C >> 2);
|
||||
} else if (pad == 2) {
|
||||
// bin[0] bin[1] bin[2]
|
||||
// +-----------+-----------+-----------+
|
||||
// | 0000 0011 ~~~~ ~~~~ ~~~~ ~~~~ |
|
||||
// +-AAAA AABB XXXX XXXX XXXX XXXX
|
||||
int A = unb64[safeAsciiPtr[charNo]];
|
||||
int B = unb64[safeAsciiPtr[charNo + 1]];
|
||||
|
||||
bin[cb++] = (A << 2) | (B >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
return bin;
|
||||
}
|
||||
|
||||
// There are some invalid unbase64 strings, even when they are comprised
|
||||
// of completely valid characters. An example is "==". That's a 0-length
|
||||
// piece of data that says it is padded by 2 bytes at the end. Well, you
|
||||
// only need to pad by 2 bytes if the number of bits in the original data
|
||||
// was not evenly divisible by 6. 0%6==0, so something's clearly wrong here.
|
||||
int base64integrity(const char *ascii, int len) {
|
||||
// The base64 string is somewhat inflated, since each ASCII character
|
||||
// represents only a 6-bit value (a sextet). That leaves 2 bits wasted per 8 bits used.
|
||||
// More importantly, for the sextet stream you're getting here (inside
|
||||
// an octet stream) to be VALID, THE LENGTH HAS TO BE A MULTIPLE OF 4.
|
||||
// You can see in the base64 function above, the algorithm always writes
|
||||
// into the final base64 string in groups of __4__.
|
||||
|
||||
// So from there, you can see a valid base64 string has just gotta have a
|
||||
// length that is a multiple of 4.
|
||||
|
||||
// If it does not, then it simply isn't valid base64 and the string should
|
||||
// be rejected. There really is little sense in trying to decode invalid
|
||||
// base64, because it's probably some kind of attack.
|
||||
|
||||
// If the length is not a multiple of 4, it's invalid base64.
|
||||
// Here, the empty string will be valid base64 because it represents empty data.
|
||||
if (len % 4)
|
||||
return 0;
|
||||
|
||||
// LOOKING FOR BAD CHARACTERS
|
||||
int i;
|
||||
for (i = 0; i < len - 2; i++) {
|
||||
if (!isbase64ValidChr(ascii[i])) {
|
||||
printf("ERROR in base64integrity at chr %d [%c]. String is NOT valid base64.\n", i, ascii[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Only last 2 can be '='
|
||||
// Check 2nd last:
|
||||
if (ascii[i] == '=') {
|
||||
// If the 2nd last is = the last MUST be = too
|
||||
if (ascii[i + 1] != '=') {
|
||||
printf("ERROR in base64integrity at chr %d.\n"
|
||||
"If the 2nd last chr is '=' then the last chr must be '=' too.\n "
|
||||
"String is NOT valid base64.",
|
||||
i);
|
||||
return 0;
|
||||
}
|
||||
} else if (!isbase64ValidChr(ascii[i])) // not = or valid base64
|
||||
{
|
||||
// 2nd last was invalid and not '='
|
||||
printf("ERROR in base64integrity at chr %d (2nd last chr). String is NOT valid base64.\n", i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check last
|
||||
|
||||
i++;
|
||||
if (ascii[i] != '=' && !isbase64ValidChr(ascii[i])) {
|
||||
printf("ERROR in base64integrity at chr %d (last chr). String is NOT valid base64.\n", i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Otherwise if get here, b64 string was valid.
|
||||
|
||||
return 1;
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: 2013 William Sherif
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// The COMPILE-TIME SETTING SAFEBASE64 is really important.
|
||||
// You need to decide if PARANOIA is more important to you than speed.
|
||||
//
|
||||
// SAFEBASE64: Remove this def to NOT check the validity of base64 ascii strings
|
||||
// before unbase64'ing that string. If you don't #define SAFEBASE64,
|
||||
// then the program assumes that all characters in the string sent to unbase64()
|
||||
// are in the base64 alphabet. As such if a character is NOT in the base64 alphabet
|
||||
// your data will be wrong (it will be turned to 0 (as if it were just a base64 'A')).
|
||||
// Removing this test greatly speeds up unbase64'ing (by about 3-4x).
|
||||
#define SAFEBASE64
|
||||
#define isMultipleOf(a, x) (!((a) % x))
|
||||
|
||||
// Converts any binary data to base64 characters.
|
||||
// Length of the resultant string is stored in flen
|
||||
// (you must pass pointer flen).
|
||||
char *base64(const void *binaryData, int len, int *flen);
|
||||
|
||||
// Convert your base64 string haJIh/+ back to binary data.
|
||||
// len is the string length and should NOT include the null terminator.
|
||||
// Final size will be stored in flen
|
||||
// (you must pass pointer flen).
|
||||
unsigned char *unbase64(const char *ascii, int len, int *flen);
|
||||
|
||||
// Checks the integrity of a base64 string to make sure it is
|
||||
// made up of only characters in the base64 alphabet (array b64)
|
||||
#define isbase64ValidChr(ch) (('0' <= ch && ch <= '9') || \
|
||||
('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || \
|
||||
ch == '+' || ch == '/') // other 2 valid chars, + ending chrs
|
||||
// = is NOT considered a valid base64 chr, it's only valid at the end for padding
|
||||
|
||||
// Tells you if a string is valid base64, which means it's length is
|
||||
// a multiple of 4, and it contains only valid base64 chrs.
|
||||
int base64integrity(const char *ascii, int len);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,375 @@
|
||||
// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <rz_types.h>
|
||||
#include <rz_lib.h>
|
||||
#include <rz_cmd.h>
|
||||
#include <rz_core.h>
|
||||
#include <rz_cons.h>
|
||||
#include <rz_analysis.h>
|
||||
|
||||
#include "jsdec.h"
|
||||
#include "jsdec-cutter.h"
|
||||
|
||||
typedef struct rz_core_t RzCore;
|
||||
|
||||
typedef struct exec_context_t {
|
||||
RzCore *core;
|
||||
void *bed;
|
||||
RzStrBuf *anno;
|
||||
JSValue shared;
|
||||
} ExecContext;
|
||||
|
||||
static JSValue js_analysis_bytes(JSContext *ctx, RzCore *core, RzCoreDecodedBytes *cdb) {
|
||||
RzAnalysisOp *aop = &cdb->an_op;
|
||||
JSValue op = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, op, "offset", JS_NewBigUint64(ctx, aop->addr));
|
||||
if (aop->ptr != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, op, "ptr", JS_NewBigUint64(ctx, aop->ptr));
|
||||
}
|
||||
if (aop->val != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, op, "val", JS_NewBigUint64(ctx, aop->val));
|
||||
}
|
||||
JS_SetPropertyStr(ctx, op, "opcode", JS_NewString(ctx, rz_str_get_null(cdb->opcode)));
|
||||
JS_SetPropertyStr(ctx, op, "disasm", JS_NewString(ctx, rz_str_get_null(cdb->disasm)));
|
||||
JS_SetPropertyStr(ctx, op, "type", JS_NewString(ctx, rz_analysis_optype_to_string(aop->type)));
|
||||
if (aop->jump != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, op, "jump", JS_NewBigInt64(ctx, aop->jump));
|
||||
}
|
||||
if (aop->fail != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, op, "fail", JS_NewBigUint64(ctx, aop->fail));
|
||||
}
|
||||
const char *comment = rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, aop->addr);
|
||||
if (RZ_STR_ISNOTEMPTY(comment)) {
|
||||
JS_SetPropertyStr(ctx, op, "comment", JS_NewString(ctx, comment));
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static JSValue js_analysis_opcodes(JSContext *ctx, RzCore *core) {
|
||||
RzCoreDecodedBytes *cdb;
|
||||
JSValue ops = JS_NewArray(ctx);
|
||||
st64 op_idx = 0;
|
||||
|
||||
RzIterator *iter = rz_core_analysis_bytes(core, core->offset, core->block, core->blocksize, 0);
|
||||
if (!iter) {
|
||||
return ops;
|
||||
}
|
||||
rz_iterator_foreach(iter, cdb) {
|
||||
if (!cdb || !strcmp(cdb->opcode, "nop")) {
|
||||
continue;
|
||||
}
|
||||
JSValue op = js_analysis_bytes(ctx, core, cdb);
|
||||
JS_SetPropertyInt64(ctx, ops, op_idx, op);
|
||||
op_idx++;
|
||||
}
|
||||
rz_iterator_free(iter);
|
||||
return ops;
|
||||
}
|
||||
|
||||
static JSValue js_function_bbs(JSContext *ctx, RzCore *core, RzAnalysisFunction *fcn) {
|
||||
JSValue bbs = JS_NewArray(ctx);
|
||||
RzAnalysisBlock *bbi;
|
||||
void **iter;
|
||||
st64 bbs_idx = 0;
|
||||
ut64 old_offset = core->offset;
|
||||
ut64 old_bsize = core->blocksize;
|
||||
rz_pvector_foreach (fcn->bbs, iter) {
|
||||
bbi = (RzAnalysisBlock *)*iter;
|
||||
rz_core_block_size(core, bbi->size);
|
||||
rz_core_seek(core, bbi->addr, true);
|
||||
|
||||
JSValue bb = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, bb, "address", JS_NewBigUint64(ctx, bbi->addr));
|
||||
if (bbi->jump != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, bb, "jump", JS_NewBigUint64(ctx, bbi->jump));
|
||||
}
|
||||
if (bbi->fail != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, bb, "fail", JS_NewBigUint64(ctx, bbi->fail));
|
||||
}
|
||||
JSValue ops = js_analysis_opcodes(ctx, core);
|
||||
JS_SetPropertyStr(ctx, bb, "ops", ops);
|
||||
JS_SetPropertyInt64(ctx, bbs, bbs_idx, bb);
|
||||
bbs_idx++;
|
||||
}
|
||||
rz_core_block_size(core, old_bsize);
|
||||
rz_core_seek(core, old_offset, true);
|
||||
return bbs;
|
||||
}
|
||||
|
||||
static JSValue js_command(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
if (argc != 1) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
const char *command = JS_ToCString(ctx, argv[0]);
|
||||
if (!command) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx);
|
||||
RzCore *core = ectx->core;
|
||||
rz_cons_sleep_end(ectx->bed);
|
||||
|
||||
char *output = rz_core_cmd_str(core, command);
|
||||
JS_FreeCString(ctx, command);
|
||||
JSValue result = JS_NewString(ctx, output ? output : "");
|
||||
free(output);
|
||||
|
||||
ectx->bed = rz_cons_sleep_begin();
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue js_graph(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
if (argc != 0) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx);
|
||||
RzCore *core = ectx->core;
|
||||
rz_cons_sleep_end(ectx->bed);
|
||||
|
||||
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, -1);
|
||||
if (!fcn) {
|
||||
ectx->bed = rz_cons_sleep_begin();
|
||||
return JS_ThrowInternalError(ctx, "Cannot find function at 0x%08" PFMT64x, core->offset);
|
||||
}
|
||||
|
||||
JSValue graph = JS_NewArray(ctx);
|
||||
JSValue object = JS_NewObject(ctx);
|
||||
JS_SetPropertyInt64(ctx, graph, 0, object);
|
||||
JS_SetPropertyStr(ctx, object, "name", JS_NewString(ctx, rz_str_get_null(fcn->name)));
|
||||
JSValue blocks = js_function_bbs(ctx, core, fcn);
|
||||
JS_SetPropertyStr(ctx, object, "blocks", blocks);
|
||||
ectx->bed = rz_cons_sleep_begin();
|
||||
return graph;
|
||||
}
|
||||
|
||||
static JSValue js_console_log(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx);
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (i != 0) {
|
||||
rz_strbuf_append_n(ectx->anno, " ", 1);
|
||||
}
|
||||
const char *str = JS_ToCString(ctx, argv[i]);
|
||||
if (!str) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
rz_strbuf_append(ectx->anno, str);
|
||||
JS_FreeCString(ctx, str);
|
||||
}
|
||||
rz_strbuf_append_n(ectx->anno, "\n", 1);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_get_global(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx);
|
||||
return JS_GetPropertyStr(ctx, ectx->shared, "Shared");
|
||||
}
|
||||
|
||||
static jsdec_t *jsdec_create(ExecContext *ec) {
|
||||
jsdec_t *dec = jsdec_new();
|
||||
if (!dec) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSContext *ctx = jsdec_context(dec);
|
||||
JS_SetContextOpaque(ctx, ec);
|
||||
ec->shared = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, ec->shared, "Shared", JS_NewObject(ctx));
|
||||
|
||||
JSValue global = JS_GetGlobalObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "Global", JS_NewCFunction(ctx, js_get_global, "Global", 1));
|
||||
|
||||
JSValue console = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "console", console);
|
||||
JS_SetPropertyStr(ctx, console, "log", JS_NewCFunction(ctx, js_console_log, "log", 1));
|
||||
|
||||
JSValue rizin = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "rizin", rizin);
|
||||
JS_SetPropertyStr(ctx, rizin, "command", JS_NewCFunction(ctx, js_command, "command", 1));
|
||||
JS_SetPropertyStr(ctx, rizin, "graph", JS_NewCFunction(ctx, js_graph, "graph", 1));
|
||||
|
||||
JSValue process = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "process", process);
|
||||
JSValue args = JS_NewArray(ctx);
|
||||
JS_SetPropertyInt64(ctx, args, 0, JS_NewString(ctx, "--annotation"));
|
||||
JS_SetPropertyStr(ctx, process, "args", args);
|
||||
|
||||
JS_FreeValue(ctx, global);
|
||||
return dec;
|
||||
}
|
||||
|
||||
static void jsdec_destroy(jsdec_t *dec, ExecContext *ec) {
|
||||
JSContext *ctx = jsdec_context(dec);
|
||||
JS_FreeValue(ctx, ec->shared);
|
||||
jsdec_free(dec);
|
||||
}
|
||||
|
||||
static char *json_to_strdup(const RzJson *object, const char *key) {
|
||||
const RzJson *j = rz_json_get(object, key);
|
||||
if (!j || j->type != RZ_JSON_STRING) {
|
||||
return NULL;
|
||||
}
|
||||
return rz_str_dup(j->str_value);
|
||||
}
|
||||
|
||||
static const char *json_as_string(const RzJson *object, const char *key) {
|
||||
const RzJson *j = rz_json_get(object, key);
|
||||
if (!j || j->type != RZ_JSON_STRING) {
|
||||
return NULL;
|
||||
}
|
||||
return j->str_value;
|
||||
}
|
||||
|
||||
static ut64 json_as_ut64(const RzJson *object, const char *key) {
|
||||
const RzJson *j = rz_json_get(object, key);
|
||||
if (!j || (j->type != RZ_JSON_STRING && j->type != RZ_JSON_INTEGER)) {
|
||||
return 0;
|
||||
}
|
||||
if (j->type == RZ_JSON_INTEGER) {
|
||||
return j->num.u_value;
|
||||
} else if (rz_num_is_valid_input(NULL, j->str_value)) {
|
||||
return rz_num_get_input_value(NULL, j->str_value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static RzAnnotatedCode *parse_json(RzStrBuf *sb) {
|
||||
char *text = rz_strbuf_drain(sb);
|
||||
if (RZ_STR_ISEMPTY(text)) {
|
||||
free(text);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// text is modified by rz_json_parse and
|
||||
// is freed by rz_json_free
|
||||
RzJson *json = rz_json_parse(text);
|
||||
if (!json) {
|
||||
RZ_LOG_ERROR("failed to parse from json string\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *raw_code = json_to_strdup(json, "code");
|
||||
if (!raw_code) {
|
||||
rz_json_free(json);
|
||||
RZ_LOG_ERROR("failed to dup code\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RzAnnotatedCode *code = rz_annotated_code_new(raw_code);
|
||||
if (!code) {
|
||||
rz_json_free(json);
|
||||
RZ_LOG_ERROR("failed to create RzAnnotatedCode\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const RzJson *annotations = rz_json_get(json, "annotations");
|
||||
const RzJson *element = NULL;
|
||||
for (size_t idx = 0; annotations && (element = rz_json_item(annotations, idx)); idx++) {
|
||||
RzCodeAnnotation annotation = { 0 };
|
||||
annotation.start = json_as_ut64(element, "start");
|
||||
annotation.end = json_as_ut64(element, "end");
|
||||
const char *type = json_as_string(element, "type");
|
||||
if (!strcmp(type, "offset")) {
|
||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_OFFSET;
|
||||
annotation.offset.offset = json_as_ut64(element, "offset");
|
||||
} else if (!strcmp(type, "function_name")) {
|
||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_NAME;
|
||||
annotation.reference.name = json_to_strdup(element, "name");
|
||||
annotation.reference.offset = json_as_ut64(element, "offset");
|
||||
} else if (!strcmp(type, "global_variable")) {
|
||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_GLOBAL_VARIABLE;
|
||||
annotation.reference.offset = json_as_ut64(element, "offset");
|
||||
} else if (!strcmp(type, "constant_variable")) {
|
||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_CONSTANT_VARIABLE;
|
||||
annotation.reference.offset = json_as_ut64(element, "offset");
|
||||
} else if (!strcmp(type, "local_variable")) {
|
||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_LOCAL_VARIABLE;
|
||||
annotation.variable.name = json_to_strdup(element, "name");
|
||||
} else if (!strcmp(type, "function_parameter")) {
|
||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_FUNCTION_PARAMETER;
|
||||
annotation.variable.name = json_to_strdup(element, "name");
|
||||
} else if (!strcmp(type, "syntax_highlight")) {
|
||||
annotation.type = RZ_CODE_ANNOTATION_TYPE_SYNTAX_HIGHLIGHT;
|
||||
const char *highlightType = json_as_string(element, "syntax_highlight");
|
||||
if (!strcmp(highlightType, "keyword")) {
|
||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_KEYWORD;
|
||||
} else if (!strcmp(highlightType, "comment")) {
|
||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_COMMENT;
|
||||
} else if (!strcmp(highlightType, "datatype")) {
|
||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_DATATYPE;
|
||||
} else if (!strcmp(highlightType, "function_name")) {
|
||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_NAME;
|
||||
} else if (!strcmp(highlightType, "function_parameter")) {
|
||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_FUNCTION_PARAMETER;
|
||||
} else if (!strcmp(highlightType, "local_variable")) {
|
||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_LOCAL_VARIABLE;
|
||||
} else if (!strcmp(highlightType, "constant_variable")) {
|
||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_CONSTANT_VARIABLE;
|
||||
} else if (!strcmp(highlightType, "global_variable")) {
|
||||
annotation.syntax_highlight.type = RZ_SYNTAX_HIGHLIGHT_TYPE_GLOBAL_VARIABLE;
|
||||
}
|
||||
}
|
||||
rz_annotated_code_add_annotation(code, &annotation);
|
||||
}
|
||||
|
||||
rz_json_free(json);
|
||||
return code;
|
||||
}
|
||||
|
||||
RZ_API void jsdec_init_config(RzCore *core) {
|
||||
RzConfig *cfg = core->config;
|
||||
|
||||
rz_config_lock(cfg, false);
|
||||
SETPREF("jsdec.asm", "false", "if true, shows pseudo next to the assembly.");
|
||||
SETPREF("jsdec.blocks", "false", "if true, shows only scopes blocks.");
|
||||
SETPREF("jsdec.casts", "false", "if false, hides all casts in the pseudo code.");
|
||||
SETPREF("jsdec.debug", "false", "do not catch exceptions in jsdec.");
|
||||
SETPREF("jsdec.highlight", "default", "highlights the current address.");
|
||||
SETPREF("jsdec.paddr", "false", "if true, all xrefs uses physical addresses compare.");
|
||||
SETPREF("jsdec.slow", "false", "load all the data before to avoid multirequests to rizin.");
|
||||
SETPREF("jsdec.xrefs", "false", "if true, shows all xrefs in the pseudo code.");
|
||||
rz_config_lock(cfg, true);
|
||||
}
|
||||
|
||||
RZ_API RzAnnotatedCode *jsdec_as_annotation(RzCore *core, ut64 addr) {
|
||||
ExecContext ectx;
|
||||
ectx.core = core;
|
||||
ectx.anno = rz_strbuf_new("");
|
||||
if (!ectx.anno) {
|
||||
rz_strbuf_free(ectx.anno);
|
||||
RZ_LOG_ERROR("failed to create RzStrBuf\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jsdec_t *dec = jsdec_create(&ectx);
|
||||
if (!dec) {
|
||||
rz_strbuf_free(ectx.anno);
|
||||
RZ_LOG_ERROR("failed to create jsdec_t\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ut64 offset = core->offset;
|
||||
if (offset != addr) {
|
||||
rz_core_seek(core, addr, true);
|
||||
}
|
||||
|
||||
ectx.bed = rz_cons_sleep_begin();
|
||||
bool ret = jsdec_run(dec);
|
||||
rz_cons_sleep_end(ectx.bed);
|
||||
|
||||
if (offset != addr) {
|
||||
rz_core_seek(core, offset, true);
|
||||
}
|
||||
|
||||
jsdec_destroy(dec, &ectx);
|
||||
if (!ret) {
|
||||
rz_strbuf_free(ectx.anno);
|
||||
RZ_LOG_ERROR("jsdec_run returned false\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return parse_json(ectx.anno);
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <rz_types.h>
|
||||
#include <rz_lib.h>
|
||||
#include <rz_cmd.h>
|
||||
#include <rz_core.h>
|
||||
#include <rz_cons.h>
|
||||
#include <rz_analysis.h>
|
||||
|
||||
#include "jsdec.h"
|
||||
|
||||
typedef struct rz_core_t RzCore;
|
||||
|
||||
typedef struct exec_context_t {
|
||||
RzCore *core;
|
||||
void *bed;
|
||||
JSValue shared;
|
||||
} ExecContext;
|
||||
|
||||
#undef RZ_API
|
||||
#define RZ_API static
|
||||
#undef RZ_IPI
|
||||
#define RZ_IPI static
|
||||
#define SETDESC(x, y) rz_config_node_desc(x, y)
|
||||
#define SETPREF(x, y, z) SETDESC(rz_config_set(cfg, x, y), z)
|
||||
|
||||
#define name_args(name) (internal_##name##_args)
|
||||
#define name_help(name) (internal_##name##_help)
|
||||
#define name_handler(name) (internal_##name##_handler)
|
||||
#define command_handler(name, arg) \
|
||||
RZ_IPI RzCmdStatus name_handler(name)(RzCore * core, int argc, const char **argv) { \
|
||||
if (argc != 1) { \
|
||||
return RZ_CMD_STATUS_WRONG_ARGS; \
|
||||
} \
|
||||
jsdec_main(core, arg); \
|
||||
return RZ_CMD_STATUS_OK; \
|
||||
}
|
||||
#define static_description_no_args(command, description) \
|
||||
static const RzCmdDescArg name_args(command)[] = { \
|
||||
{ 0 }, \
|
||||
}; \
|
||||
static const RzCmdDescHelp name_help(command) = { \
|
||||
.summary = description, \
|
||||
.args = name_args(command), \
|
||||
}
|
||||
#define rz_cmd_desc_argv_new_warn(rcmd, root, cmd) \
|
||||
rz_warn_if_fail(rz_cmd_desc_argv_new(rcmd, root, #cmd, name_handler(cmd), &name_help(cmd)))
|
||||
|
||||
static JSValue js_analysis_bytes(JSContext *ctx, RzCore *core, RzCoreDecodedBytes *cdb) {
|
||||
RzAnalysisOp *aop = &cdb->an_op;
|
||||
JSValue op = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, op, "offset", JS_NewBigUint64(ctx, aop->addr));
|
||||
if (aop->ptr != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, op, "ptr", JS_NewBigUint64(ctx, aop->ptr));
|
||||
}
|
||||
if (aop->val != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, op, "val", JS_NewBigUint64(ctx, aop->val));
|
||||
}
|
||||
JS_SetPropertyStr(ctx, op, "opcode", JS_NewString(ctx, rz_str_get_null(cdb->opcode)));
|
||||
JS_SetPropertyStr(ctx, op, "disasm", JS_NewString(ctx, rz_str_get_null(cdb->disasm)));
|
||||
JS_SetPropertyStr(ctx, op, "type", JS_NewString(ctx, rz_analysis_optype_to_string(aop->type)));
|
||||
if (aop->jump != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, op, "jump", JS_NewBigInt64(ctx, aop->jump));
|
||||
}
|
||||
if (aop->fail != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, op, "fail", JS_NewBigUint64(ctx, aop->fail));
|
||||
}
|
||||
const char *comment = rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, aop->addr);
|
||||
if (RZ_STR_ISNOTEMPTY(comment)) {
|
||||
JS_SetPropertyStr(ctx, op, "comment", JS_NewString(ctx, comment));
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
static JSValue js_analysis_opcodes(JSContext *ctx, RzCore *core) {
|
||||
RzCoreDecodedBytes *cdb;
|
||||
JSValue ops = JS_NewArray(ctx);
|
||||
st64 op_idx = 0;
|
||||
|
||||
RzIterator *iter = rz_core_analysis_bytes(core, core->offset, core->block, core->blocksize, 0);
|
||||
if (!iter) {
|
||||
return ops;
|
||||
}
|
||||
rz_iterator_foreach (iter, cdb) {
|
||||
if (!cdb || !strcmp(cdb->opcode, "nop")) {
|
||||
continue;
|
||||
}
|
||||
JSValue op = js_analysis_bytes(ctx, core, cdb);
|
||||
JS_SetPropertyInt64(ctx, ops, op_idx, op);
|
||||
op_idx++;
|
||||
}
|
||||
rz_iterator_free(iter);
|
||||
return ops;
|
||||
}
|
||||
|
||||
static JSValue js_function_bbs(JSContext *ctx, RzCore *core, RzAnalysisFunction *fcn) {
|
||||
JSValue bbs = JS_NewArray(ctx);
|
||||
RzAnalysisBlock *bbi;
|
||||
void **iter;
|
||||
st64 bbs_idx = 0;
|
||||
ut64 old_offset = core->offset;
|
||||
ut64 old_bsize = core->blocksize;
|
||||
rz_pvector_foreach (fcn->bbs, iter) {
|
||||
bbi = (RzAnalysisBlock *)*iter;
|
||||
rz_core_block_size(core, bbi->size);
|
||||
rz_core_seek(core, bbi->addr, true);
|
||||
|
||||
JSValue bb = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, bb, "address", JS_NewBigUint64(ctx, bbi->addr));
|
||||
if (bbi->jump != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, bb, "jump", JS_NewBigUint64(ctx, bbi->jump));
|
||||
}
|
||||
if (bbi->fail != UT64_MAX) {
|
||||
JS_SetPropertyStr(ctx, bb, "fail", JS_NewBigUint64(ctx, bbi->fail));
|
||||
}
|
||||
JSValue ops = js_analysis_opcodes(ctx, core);
|
||||
JS_SetPropertyStr(ctx, bb, "ops", ops);
|
||||
JS_SetPropertyInt64(ctx, bbs, bbs_idx, bb);
|
||||
bbs_idx++;
|
||||
}
|
||||
rz_core_block_size(core, old_bsize);
|
||||
rz_core_seek(core, old_offset, true);
|
||||
return bbs;
|
||||
}
|
||||
|
||||
static JSValue js_command(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
if (argc != 1) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
const char *command = JS_ToCString(ctx, argv[0]);
|
||||
if (!command) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx);
|
||||
RzCore *core = ectx->core;
|
||||
rz_cons_sleep_end(ectx->bed);
|
||||
|
||||
char *output = rz_core_cmd_str(core, command);
|
||||
JS_FreeCString(ctx, command);
|
||||
JSValue result = JS_NewString(ctx, output ? output : "");
|
||||
free(output);
|
||||
|
||||
ectx->bed = rz_cons_sleep_begin();
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue js_graph(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
if (argc != 0) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx);
|
||||
RzCore *core = ectx->core;
|
||||
rz_cons_sleep_end(ectx->bed);
|
||||
|
||||
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, -1);
|
||||
if (!fcn) {
|
||||
ectx->bed = rz_cons_sleep_begin();
|
||||
return JS_ThrowInternalError(ctx, "Cannot find function at 0x%08" PFMT64x, core->offset);
|
||||
}
|
||||
|
||||
JSValue graph = JS_NewArray(ctx);
|
||||
JSValue object = JS_NewObject(ctx);
|
||||
JS_SetPropertyInt64(ctx, graph, 0, object);
|
||||
JS_SetPropertyStr(ctx, object, "name", JS_NewString(ctx, rz_str_get_null(fcn->name)));
|
||||
JSValue blocks = js_function_bbs(ctx, core, fcn);
|
||||
JS_SetPropertyStr(ctx, object, "blocks", blocks);
|
||||
ectx->bed = rz_cons_sleep_begin();
|
||||
return graph;
|
||||
}
|
||||
|
||||
static JSValue js_console_log(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (i != 0) {
|
||||
rz_cons_print(" ");
|
||||
}
|
||||
const char *str = JS_ToCString(ctx, argv[i]);
|
||||
if (!str) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
rz_cons_print(str);
|
||||
JS_FreeCString(ctx, str);
|
||||
}
|
||||
rz_cons_newline();
|
||||
rz_cons_flush();
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_get_global(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
ExecContext *ectx = (ExecContext *)JS_GetContextOpaque(ctx);
|
||||
return JS_GetPropertyStr(ctx, ectx->shared, "Shared");
|
||||
}
|
||||
|
||||
static jsdec_t *jsdec_create(ExecContext *ec, const char *arg) {
|
||||
jsdec_t *dec = jsdec_new();
|
||||
if (!dec) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSContext *ctx = jsdec_context(dec);
|
||||
JS_SetContextOpaque(ctx, ec);
|
||||
ec->shared = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, ec->shared, "Shared", JS_NewObject(ctx));
|
||||
|
||||
JSValue global = JS_GetGlobalObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "Global", JS_NewCFunction(ctx, js_get_global, "Global", 1));
|
||||
|
||||
JSValue console = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "console", console);
|
||||
JS_SetPropertyStr(ctx, console, "log", JS_NewCFunction(ctx, js_console_log, "log", 1));
|
||||
|
||||
JSValue rizin = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "rizin", rizin);
|
||||
JS_SetPropertyStr(ctx, rizin, "command", JS_NewCFunction(ctx, js_command, "command", 1));
|
||||
JS_SetPropertyStr(ctx, rizin, "graph", JS_NewCFunction(ctx, js_graph, "graph", 1));
|
||||
|
||||
JSValue process = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "process", process);
|
||||
JSValue args = JS_NewArray(ctx);
|
||||
if (RZ_STR_ISNOTEMPTY(arg)) {
|
||||
JS_SetPropertyInt64(ctx, args, 0, JS_NewString(ctx, arg));
|
||||
}
|
||||
JS_SetPropertyStr(ctx, process, "args", args);
|
||||
|
||||
JS_FreeValue(ctx, global);
|
||||
return dec;
|
||||
}
|
||||
|
||||
static void jsdec_destroy(jsdec_t *dec, ExecContext *ec) {
|
||||
JSContext *ctx = jsdec_context(dec);
|
||||
JS_FreeValue(ctx, ec->shared);
|
||||
jsdec_free(dec);
|
||||
}
|
||||
|
||||
static bool jsdec_main(RzCore *core, const char *arg) {
|
||||
ExecContext ectx;
|
||||
ectx.core = core;
|
||||
|
||||
jsdec_t *dec = jsdec_create(&ectx, arg);
|
||||
if (!dec) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ectx.bed = rz_cons_sleep_begin();
|
||||
bool ret = jsdec_run(dec);
|
||||
rz_cons_sleep_end(ectx.bed);
|
||||
|
||||
jsdec_destroy(dec, &ectx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const RzCmdDescHelp pdd_usage = {
|
||||
.summary = "Core plugin for jsdec",
|
||||
};
|
||||
|
||||
// static_description_no_args(cmd_pdd_star,"decompiled code is returned to rizin as comment (via CCu)");
|
||||
static_description_no_args(pdd, "decompile current function");
|
||||
static_description_no_args(pddt, "lists the supported architectures");
|
||||
static_description_no_args(pddc, "decompiled code is returned to rizin as 'file:line code' (via CL)");
|
||||
static_description_no_args(pdda, "decompile current function with side assembly");
|
||||
static_description_no_args(pddb, "decompile current function but show only scopes");
|
||||
static_description_no_args(pddo, "decompile current function side by side with offsets");
|
||||
static_description_no_args(pddj, "decompile current function as json");
|
||||
static_description_no_args(pddA, "decompile current function with annotation output");
|
||||
static_description_no_args(pddf, "decompile all functions");
|
||||
static_description_no_args(pddi, "generate issue data");
|
||||
|
||||
command_handler(pdd, NULL);
|
||||
command_handler(pddt, "--architectures");
|
||||
command_handler(pdda, "--assembly");
|
||||
command_handler(pddA, "--annotation");
|
||||
command_handler(pddb, "--blocks");
|
||||
command_handler(pddc, "--as-code-line");
|
||||
command_handler(pddf, "--all-functions");
|
||||
command_handler(pddi, "--issue");
|
||||
command_handler(pddj, "--as-json");
|
||||
command_handler(pddo, "--offsets");
|
||||
|
||||
static bool rz_cmd_pdd_init(RzCore *core, void **user) {
|
||||
(void)user;
|
||||
RzCmd *rcmd = core->rcmd;
|
||||
RzConfig *cfg = core->config;
|
||||
RzCmdDesc *root_cd = rz_cmd_get_desc(rcmd, "pd");
|
||||
if (!root_cd) {
|
||||
rz_warn_if_reached();
|
||||
return false;
|
||||
}
|
||||
|
||||
rz_config_lock(cfg, false);
|
||||
SETPREF("jsdec.asm", "false", "if true, shows pseudo next to the assembly.");
|
||||
SETPREF("jsdec.blocks", "false", "if true, shows only scopes blocks.");
|
||||
SETPREF("jsdec.casts", "false", "if false, hides all casts in the pseudo code.");
|
||||
SETPREF("jsdec.debug", "false", "do not catch exceptions in jsdec.");
|
||||
SETPREF("jsdec.highlight", "default", "highlights the current address.");
|
||||
SETPREF("jsdec.paddr", "false", "if true, all xrefs uses physical addresses compare.");
|
||||
SETPREF("jsdec.slow", "false", "load all the data before to avoid multirequests to rizin.");
|
||||
SETPREF("jsdec.xrefs", "false", "if true, shows all xrefs in the pseudo code.");
|
||||
rz_config_lock(cfg, true);
|
||||
|
||||
RzCmdDesc *pdd = rz_cmd_desc_group_new(rcmd, root_cd, "pdd", name_handler(pdd), &name_help(pdd), &pdd_usage);
|
||||
if (!pdd) {
|
||||
rz_warn_if_reached();
|
||||
return false;
|
||||
}
|
||||
|
||||
rz_cmd_desc_argv_new_warn(rcmd, pdd, pddt);
|
||||
rz_cmd_desc_argv_new_warn(rcmd, pdd, pdda);
|
||||
rz_cmd_desc_argv_new_warn(rcmd, pdd, pddA);
|
||||
rz_cmd_desc_argv_new_warn(rcmd, pdd, pddb);
|
||||
rz_cmd_desc_argv_new_warn(rcmd, pdd, pddc);
|
||||
rz_cmd_desc_argv_new_warn(rcmd, pdd, pddf);
|
||||
rz_cmd_desc_argv_new_warn(rcmd, pdd, pddi);
|
||||
rz_cmd_desc_argv_new_warn(rcmd, pdd, pddj);
|
||||
rz_cmd_desc_argv_new_warn(rcmd, pdd, pddo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool rz_cmd_pdd_fini(RzCore *core, void *user) {
|
||||
(void)user;
|
||||
RzCmd *rcmd = core->rcmd;
|
||||
RzCmdDesc *cd = rz_cmd_get_desc(rcmd, "pdd");
|
||||
return rz_cmd_desc_remove(rcmd, cd);
|
||||
}
|
||||
|
||||
RzCorePlugin rz_core_plugin_jsdec = {
|
||||
.name = "jsdec",
|
||||
.author = "deroad",
|
||||
.desc = "Pseudo-code decompiler for rizin",
|
||||
.license = "BSD-3-Clause",
|
||||
.init = rz_cmd_pdd_init,
|
||||
.fini = rz_cmd_pdd_fini,
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _RZ_API __declspec(dllexport)
|
||||
#else
|
||||
#define _RZ_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#ifndef CORELIB
|
||||
_RZ_API RzLibStruct rizin_plugin = {
|
||||
.type = RZ_LIB_TYPE_CORE,
|
||||
.data = &rz_core_plugin_jsdec,
|
||||
.version = RZ_VERSION,
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "jsdec.h"
|
||||
#define errorf(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
static JSValue shared;
|
||||
|
||||
int is_regular_file(const char *path) {
|
||||
if (!path || !*path) {
|
||||
return 0;
|
||||
}
|
||||
struct stat path_stat;
|
||||
stat(path, &path_stat);
|
||||
return S_ISREG(path_stat.st_mode);
|
||||
}
|
||||
|
||||
static char *read_file(const char *filename) {
|
||||
FILE *fp = NULL;
|
||||
long size = 0;
|
||||
char *buffer = NULL;
|
||||
|
||||
if (!is_regular_file(filename)) {
|
||||
errorf("Error: path '%s' is not a file\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fp = fopen(filename, "rb");
|
||||
if (!fp) {
|
||||
errorf("Error: failed opening file '%s'\n", filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_END); // non-portable
|
||||
size = ftell(fp);
|
||||
rewind(fp);
|
||||
if (size < 1) {
|
||||
errorf("Error: negative size (%ld) of '%s'\n", size, filename);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(buffer = malloc(size + 1))) {
|
||||
errorf("Error: ENOMEM (%ld bytes) of '%s'\n", size, filename);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
int read_bytes = fread(buffer, 1, size, fp);
|
||||
if (read_bytes != size) {
|
||||
errorf("Error: failed to read (%d/%ld bytes) from '%s'\n", read_bytes, size, filename);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// + 1 as on malloc
|
||||
buffer[size] = 0;
|
||||
fclose(fp);
|
||||
return buffer;
|
||||
|
||||
fail:
|
||||
fclose(fp);
|
||||
free(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static JSValue js_console_log(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (i != 0) {
|
||||
fputc(' ', stdout);
|
||||
}
|
||||
const char *str = JS_ToCString(ctx, argv[i]);
|
||||
if (!str) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
fputs(str, stdout);
|
||||
JS_FreeCString(ctx, str);
|
||||
}
|
||||
fputc('\n', stdout);
|
||||
fflush(stdout);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue js_get_global(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
return JS_GetPropertyStr(ctx, shared, "Shared");
|
||||
}
|
||||
|
||||
static int init_testsuite(jsdec_t *dec, const char *file, const char *raw) {
|
||||
JSContext *ctx = jsdec_context(dec);
|
||||
|
||||
shared = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, shared, "Shared", JS_NewObject(ctx));
|
||||
|
||||
JSValue global = JS_GetGlobalObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "Global", JS_NewCFunction(ctx, js_get_global, "Global", 1));
|
||||
JS_SetPropertyStr(ctx, global, "rizin", JS_NULL);
|
||||
|
||||
JSValue console = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "console", console);
|
||||
JS_SetPropertyStr(ctx, console, "log", JS_NewCFunction(ctx, js_console_log, "log", 1));
|
||||
|
||||
JSValue unit = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "unit", unit);
|
||||
JS_SetPropertyStr(ctx, unit, "file", JS_NewString(ctx, file));
|
||||
JS_SetPropertyStr(ctx, unit, "raw", JS_NewString(ctx, raw));
|
||||
|
||||
JS_FreeValue(ctx, global);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void fini_testsuite(jsdec_t *dec) {
|
||||
JSContext *ctx = jsdec_context(dec);
|
||||
JS_FreeValue(ctx, shared);
|
||||
jsdec_free(dec);
|
||||
}
|
||||
|
||||
int main(int argc, char const *argv[]) {
|
||||
if (argc != 2 || !strcmp(argv[1], "-h")) {
|
||||
errorf("usage: %s <issue.json>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
jsdec_t *dec = NULL;
|
||||
char *raw = read_file(argv[1]);
|
||||
if (!raw) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(dec = jsdec_new())) {
|
||||
free(raw);
|
||||
return 1;
|
||||
}
|
||||
|
||||
init_testsuite(dec, argv[1], raw);
|
||||
free(raw);
|
||||
|
||||
int ret = !jsdec_run(dec);
|
||||
|
||||
fini_testsuite(dec);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
// SPDX-FileCopyrightText: 2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "base64.h"
|
||||
#include "jsdec.h"
|
||||
#include "js/bytecode.h"
|
||||
|
||||
struct jsdec_s {
|
||||
JSRuntime *runtime;
|
||||
JSContext *context;
|
||||
};
|
||||
|
||||
#define macro_str(s) #s
|
||||
#define errorf(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
void js_print_exception(JSContext *ctx, JSValueConst val) {
|
||||
const char *strval = JS_ToCString(ctx, val);
|
||||
if (strval) {
|
||||
fprintf(stderr, "%s\n", strval);
|
||||
JS_FreeCString(ctx, strval);
|
||||
} else {
|
||||
fprintf(stderr, "[exception]\n");
|
||||
}
|
||||
}
|
||||
|
||||
void jsdec_handle_exception(JSContext *ctx) {
|
||||
JSValue exception = JS_GetException(ctx);
|
||||
int is_error = JS_IsError(ctx, exception);
|
||||
|
||||
js_print_exception(ctx, exception);
|
||||
if (!is_error) {
|
||||
JS_FreeValue(ctx, exception);
|
||||
return;
|
||||
}
|
||||
|
||||
JSValue stack = JS_GetPropertyStr(ctx, exception, "stack");
|
||||
if (!JS_IsUndefined(stack)) {
|
||||
js_print_exception(ctx, stack);
|
||||
}
|
||||
JS_FreeValue(ctx, stack);
|
||||
JS_FreeValue(ctx, exception);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void print_jsval(JSValue val) {
|
||||
if(JS_IsNumber(val)) {
|
||||
errorf("JS_IsNumber\n");
|
||||
} else if(JS_IsBigInt(NULL, val)) {
|
||||
errorf("JS_IsBigInt\n");
|
||||
} else if(JS_IsBool(val)) {
|
||||
errorf("JS_IsBool\n");
|
||||
} else if(JS_IsNull(val)) {
|
||||
errorf("JS_IsNull\n");
|
||||
} else if(JS_IsUndefined(val)) {
|
||||
errorf("JS_IsUndefined\n");
|
||||
} else if(JS_IsException(val)) {
|
||||
errorf("JS_IsException\n");
|
||||
} else if(JS_IsUninitialized(val)) {
|
||||
errorf("JS_IsUninitialized\n");
|
||||
} else if(JS_IsString(val)) {
|
||||
errorf("JS_IsString\n");
|
||||
} else if(JS_IsSymbol(val)) {
|
||||
errorf("JS_IsSymbol\n");
|
||||
} else if(JS_IsObject(val)) {
|
||||
errorf("JS_IsObject\n");
|
||||
} else if(JS_VALUE_GET_TAG(val) == JS_TAG_MODULE) {
|
||||
errorf("Module\n");
|
||||
} else if(JS_VALUE_GET_TAG(val) == JS_TAG_FUNCTION_BYTECODE) {
|
||||
errorf("Function bytecode\n");
|
||||
} else {
|
||||
errorf("No idea...\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int js_add_module(JSContext *ctx, const uint8_t *bytecode, const uint32_t size) {
|
||||
JSValue obj = JS_ReadObject(ctx, bytecode, size, JS_READ_OBJ_BYTECODE);
|
||||
if (JS_IsException(obj)) {
|
||||
jsdec_handle_exception(ctx);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int js_load_module(JSContext *ctx, const uint8_t *bytes, const uint32_t size) {
|
||||
JSValue obj = JS_ReadObject(ctx, bytes, size, JS_READ_OBJ_BYTECODE);
|
||||
if (JS_IsException(obj)) {
|
||||
jsdec_handle_exception(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE &&
|
||||
JS_ResolveModule(ctx, obj) < 0) {
|
||||
JS_FreeValue(ctx, obj);
|
||||
errorf("Error: failed to resolve jsdec module\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//print_jsval(obj);
|
||||
JSValue val = JS_EvalFunction(ctx, obj);
|
||||
if (JS_IsException(val)) {
|
||||
errorf("Error: jsdec thrown an exception when loading a module\n");
|
||||
jsdec_handle_exception(ctx);
|
||||
return 0;
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "js/bytecode_mod.h"
|
||||
|
||||
void jsdec_free(jsdec_t *dec) {
|
||||
if (!dec) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS_FreeContext(dec->context);
|
||||
JS_FreeRuntime(dec->runtime);
|
||||
free(dec);
|
||||
}
|
||||
|
||||
static JSValue js_atob(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
if (argc != 1 || !JS_IsString(argv[0])) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
const char *encoded = JS_ToCString(ctx, argv[0]);
|
||||
if (!encoded) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
int length = (int)strlen(encoded);
|
||||
if (length < 1 || !base64integrity(encoded, length)) {
|
||||
JS_FreeCString(ctx, encoded);
|
||||
return JS_ThrowInternalError(ctx, "Invalid base64 string");
|
||||
}
|
||||
|
||||
int flen = 0;
|
||||
char *decoded = (char *)unbase64(encoded, length, &flen);
|
||||
JS_FreeCString(ctx, encoded);
|
||||
|
||||
JSValue result = JS_NewStringLen(ctx, decoded, flen);
|
||||
free(decoded);
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue js_btoa(JSContext *ctx, JSValueConst jsThis, int argc, JSValueConst *argv) {
|
||||
if (argc != 1 || !JS_IsString(argv[0])) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
const char *decoded = JS_ToCString(ctx, argv[0]);
|
||||
if (!decoded) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
int length = (int)strlen(decoded);
|
||||
if (length < 0) {
|
||||
JS_FreeCString(ctx, decoded);
|
||||
return JS_ThrowInternalError(ctx, "Invalid string");
|
||||
} else if (!length) {
|
||||
JS_FreeCString(ctx, decoded);
|
||||
return JS_NewString(ctx, "");
|
||||
}
|
||||
|
||||
int flen = 0;
|
||||
char *encoded = base64(decoded, length, &flen);
|
||||
JS_FreeCString(ctx, decoded);
|
||||
|
||||
JSValue result = JS_NewStringLen(ctx, encoded, flen);
|
||||
free(encoded);
|
||||
return result;
|
||||
}
|
||||
|
||||
jsdec_t *jsdec_new() {
|
||||
JSRuntime *rt = JS_NewRuntime();
|
||||
if (!rt) {
|
||||
errorf("Error: failed to create qjs runtime\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSContext *ctx = JS_NewContextRaw(rt);
|
||||
if (!ctx) {
|
||||
errorf("Error: failed to create qjs context\n");
|
||||
JS_FreeRuntime(rt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// initialize all intrisic
|
||||
JS_AddIntrinsicBaseObjects(ctx);
|
||||
JS_AddIntrinsicEval(ctx);
|
||||
JS_AddIntrinsicRegExpCompiler(ctx);
|
||||
JS_AddIntrinsicRegExp(ctx);
|
||||
JS_AddIntrinsicJSON(ctx);
|
||||
JS_AddIntrinsicTypedArrays(ctx);
|
||||
JS_AddIntrinsicPromise(ctx);
|
||||
JS_AddIntrinsicBigInt(ctx);
|
||||
JS_AddIntrinsicDate(ctx);
|
||||
|
||||
|
||||
// Setup global objects.
|
||||
JSValue global = JS_GetGlobalObject(ctx);
|
||||
|
||||
JS_SetPropertyStr(ctx, global, "atob", JS_NewCFunction(ctx, js_atob, "atob", 1));
|
||||
JS_SetPropertyStr(ctx, global, "btoa", JS_NewCFunction(ctx, js_btoa, "btoa", 1));
|
||||
|
||||
JSValue limits = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, global, "Limits", limits);
|
||||
JS_SetPropertyStr(ctx, limits, "UT16_MAX", JS_NewBigUint64(ctx, 0xFFFFu));
|
||||
JS_SetPropertyStr(ctx, limits, "UT32_MAX", JS_NewBigUint64(ctx, 0xFFFFFFFFu));
|
||||
JS_SetPropertyStr(ctx, limits, "UT64_MAX", JS_NewBigUint64(ctx, 0xFFFFFFFFFFFFFFFFull));
|
||||
JS_SetPropertyStr(ctx, limits, "ST16_MAX", JS_NewBigInt64(ctx, 0x7FFF));
|
||||
JS_SetPropertyStr(ctx, limits, "ST32_MAX", JS_NewBigInt64(ctx, 0x7FFFFFFF));
|
||||
JS_SetPropertyStr(ctx, limits, "ST64_MAX", JS_NewBigInt64(ctx, 0x7FFFFFFFFFFFFFFFull));
|
||||
JS_FreeValue(ctx, global);
|
||||
|
||||
if (!js_load_all_modules(ctx)) {
|
||||
errorf("Error: failed to load modules\n");
|
||||
JS_FreeContext(ctx);
|
||||
JS_FreeRuntime(rt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jsdec_t *dec = malloc(sizeof(jsdec_t));
|
||||
if (!dec) {
|
||||
errorf("Error: failed to allocate jsdec_t\n");
|
||||
JS_FreeContext(ctx);
|
||||
JS_FreeRuntime(rt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dec->context = ctx;
|
||||
dec->runtime = rt;
|
||||
return dec;
|
||||
}
|
||||
|
||||
JSContext *jsdec_context(const jsdec_t *dec) {
|
||||
return dec->context;
|
||||
}
|
||||
|
||||
int jsdec_run(const jsdec_t *dec) {
|
||||
return js_load_module(dec->context, main_bytecode, main_bytecode_size);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// SPDX-FileCopyrightText: 2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#ifndef JSDEC_H
|
||||
#define JSDEC_H
|
||||
|
||||
#include <quickjs.h>
|
||||
|
||||
typedef struct jsdec_s jsdec_t;
|
||||
|
||||
void jsdec_free(jsdec_t *dec);
|
||||
jsdec_t *jsdec_new();
|
||||
JSContext *jsdec_context(const jsdec_t *dec);
|
||||
int jsdec_run(const jsdec_t *dec);
|
||||
void jsdec_handle_exception(JSContext *ctx);
|
||||
|
||||
#endif /* JSDEC_H */
|
||||
@@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
project(cutter-jsdec-plugin)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
set(CUTTER_INSTALL_PLUGDIR "share/rizin/cutter/plugins/native" CACHE STRING "Directory to install Cutter plugin into")
|
||||
set(JSDEC_BUILD_DIR "../build" CACHE STRING "Directory where to find libjsdec.a")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_INSTALL_PREFIX}/lib/cmake/Cutter")
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
add_library(jsdec_cutter MODULE
|
||||
JSDecPlugin.h
|
||||
JSDecPlugin.cpp
|
||||
JSDecDecompiler.h
|
||||
JSDecDecompiler.cpp
|
||||
)
|
||||
|
||||
find_library(libjsdec NAMES jsdec PATHS "${JSDEC_BUILD_DIR}")
|
||||
include_directories("../include")
|
||||
|
||||
find_package(Cutter REQUIRED)
|
||||
|
||||
target_link_libraries(jsdec_cutter PRIVATE Cutter::Cutter "${libjsdec}")
|
||||
install(TARGETS jsdec_cutter DESTINATION "${CUTTER_INSTALL_PLUGDIR}")
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include "JSDecDecompiler.h"
|
||||
#include <Cutter.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
|
||||
#include "jsdec-cutter.h"
|
||||
|
||||
JSDecDecompiler::JSDecDecompiler(CutterCore *core)
|
||||
: Decompiler("jsdec", "jsdec", core) {
|
||||
task = nullptr;
|
||||
RzCoreLocked c = core->core();
|
||||
jsdec_init_config(c);
|
||||
}
|
||||
|
||||
void JSDecDecompiler::decompileAt(RVA addr) {
|
||||
if (task) {
|
||||
return;
|
||||
}
|
||||
|
||||
task = new RizinFunctionTask([addr](RzCore *core) {
|
||||
return jsdec_as_annotation(core, addr);
|
||||
});
|
||||
|
||||
connect(task, &RizinFunctionTask::finished, this, [this]() {
|
||||
auto res = reinterpret_cast<RzAnnotatedCode *>(task->getResult());
|
||||
delete task;
|
||||
task = nullptr;
|
||||
if (!res) {
|
||||
res = Decompiler::makeWarning(tr("Failed to parse JSON from jsdec"));
|
||||
}
|
||||
emit finished(res);
|
||||
});
|
||||
|
||||
task->startTask();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#ifndef JSDEC_DECOMPILER_H
|
||||
#define JSDEC_DECOMPILER_H
|
||||
|
||||
#include "Decompiler.h"
|
||||
#include "RizinTask.h"
|
||||
|
||||
class JSDecDecompiler: public Decompiler
|
||||
{
|
||||
private:
|
||||
RizinFunctionTask *task;
|
||||
|
||||
public:
|
||||
JSDecDecompiler(CutterCore *core = nullptr);
|
||||
virtual void decompileAt(RVA addr) override;
|
||||
virtual bool isRunning() override { return task != nullptr; }
|
||||
};
|
||||
|
||||
#endif // JSDEC_DECOMPILER_H
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include "JSDecDecompiler.h"
|
||||
#include "JSDecPlugin.h"
|
||||
|
||||
void JSDecPlugin::setupPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
void JSDecPlugin::setupInterface(MainWindow *)
|
||||
{
|
||||
}
|
||||
|
||||
void JSDecPlugin::registerDecompilers()
|
||||
{
|
||||
Core()->registerDecompiler(new JSDecDecompiler(Core()));
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#ifndef JSDEC_CUTTER_PLUGIN_H
|
||||
#define JSDEC_CUTTER_PLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtPlugin>
|
||||
#include <plugins/CutterPlugin.h>
|
||||
|
||||
class JSDecPlugin : public QObject, CutterPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "re.rizin.cutter.plugins.jsdec")
|
||||
Q_INTERFACES(CutterPlugin)
|
||||
|
||||
public:
|
||||
void setupPlugin() override;
|
||||
void setupInterface(MainWindow *main) override;
|
||||
void registerDecompilers() override;
|
||||
|
||||
QString getName() const override { return "JsDec Decompiler (jsdec)"; }
|
||||
QString getAuthor() const override { return "deroad"; }
|
||||
QString getDescription() const override { return "GUI Integration of jsdec."; }
|
||||
QString getVersion() const override { return "1.0"; }
|
||||
};
|
||||
|
||||
|
||||
#endif // JSDEC_CUTTER_PLUGIN_H
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
find . -type f -name "*.js" | xargs eslint
|
||||
find js/ -type f -name "*.js" | xargs eslint
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: 2024 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#ifndef JSDEC_CUTTER_H
|
||||
#define JSDEC_CUTTER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <rz_util/rz_annotated_code.h>
|
||||
|
||||
RZ_API void jsdec_init_config(RzCore *core);
|
||||
RZ_API RzAnnotatedCode *jsdec_as_annotation(RzCore *core, ut64 addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* JSDEC_CUTTER_H */
|
||||
@@ -0,0 +1,109 @@
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2017-2018 pancake <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
|
||||
import libdec from './libdec/libdec.js';
|
||||
import rzutil from './libdec/rzutil.js';
|
||||
import rzpipe from './libdec/rzpipe.js';
|
||||
import Warning from './libdec/warning.js';
|
||||
import Printer from './libdec/printer.js';
|
||||
|
||||
/**
|
||||
* Shared data accessible from everywhere.
|
||||
* @type {Object}
|
||||
*/
|
||||
var Shared = Global();
|
||||
Shared.evars = null;
|
||||
Shared.context = null;
|
||||
Shared.printer = null;
|
||||
Shared.argdb = null;
|
||||
Shared.xrefs = null;
|
||||
Shared.warning = Warning;
|
||||
|
||||
function decompile_offset(architecture, fcnname) {
|
||||
var data = new rzutil.data();
|
||||
Shared.argdb = data.argdb;
|
||||
// af seems to break renaming.
|
||||
/* asm.pseudo breaks things.. */
|
||||
if (data.graph && data.graph.length > 0) {
|
||||
var p = new libdec.core.session(data, architecture);
|
||||
var arch_context = architecture.context(data);
|
||||
libdec.core.analysis.pre(p, architecture, arch_context);
|
||||
libdec.core.decompile(p, architecture, arch_context);
|
||||
libdec.core.analysis.post(p, architecture, arch_context);
|
||||
libdec.core.print(p);
|
||||
} else if (Shared.evars.extra.allfunctions) {
|
||||
Shared.context.printLog('Error: Please analyze the ' + fcnname + ' first.', true);
|
||||
} else {
|
||||
Shared.context.printLog('Error: no data available.\nPlease analyze the function/binary first.', true);
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
var lines = null;
|
||||
var errors = [];
|
||||
var log = [];
|
||||
try {
|
||||
Shared.evars = rzutil.evars(process.args);
|
||||
rzutil.sanitize(true, Shared.evars);
|
||||
if (rzutil.check_args(process.args)) {
|
||||
rzutil.sanitize(false, Shared.evars);
|
||||
return;
|
||||
}
|
||||
|
||||
// theme (requires to be initialized after evars)
|
||||
Shared.printer = new Printer();
|
||||
|
||||
var architecture = libdec.archs[Shared.evars.arch];
|
||||
|
||||
if (architecture) {
|
||||
Shared.context = new libdec.context();
|
||||
var current = rzpipe.long('s');
|
||||
if (Shared.evars.extra.allfunctions) {
|
||||
var functions = rzpipe.json('aflj');
|
||||
functions.forEach(function(x) {
|
||||
if (x.name.startsWith('sym.imp.') || x.name.startsWith('loc.imp.')) {
|
||||
return;
|
||||
}
|
||||
rzpipe.string('s 0x' + x.offset.toString(16));
|
||||
Shared.context.printLine("", x.offset);
|
||||
Shared.context.printLine(Shared.printer.theme.comment('/* name: ' + x.name + ' @ 0x' + x.offset.toString(16) + ' */'), x.offset);
|
||||
decompile_offset(architecture, x.name);
|
||||
});
|
||||
rzpipe.string('s 0x' + current.toString(16));
|
||||
var o = Shared.context;
|
||||
Shared.context = new libdec.context();
|
||||
Shared.context.macros = o.macros;
|
||||
Shared.context.dependencies = o.dependencies;
|
||||
Shared.context.printLine(Shared.printer.theme.comment('/* jsdec pseudo code output */'), current);
|
||||
Shared.context.printLine(Shared.printer.theme.comment('/* ' + Shared.evars.extra.file + ' */'), current);
|
||||
if (['java', 'dalvik'].indexOf(Shared.evars.arch) < 0) {
|
||||
Shared.context.printMacros(true);
|
||||
Shared.context.printDependencies(true);
|
||||
}
|
||||
o.lines = Shared.context.lines.concat(o.lines);
|
||||
Shared.context = o;
|
||||
} else {
|
||||
decompile_offset(architecture, null);
|
||||
}
|
||||
errors = errors.concat(Shared.context.errors);
|
||||
log = log.concat(Shared.context.log);
|
||||
lines = Shared.context.lines;
|
||||
} else {
|
||||
errors.push(Shared.evars.arch + ' is not currently supported.\n' +
|
||||
'Please open an enhancement issue at https://github.com/rizinorg/jsdec/issues\n' +
|
||||
libdec.supported());
|
||||
}
|
||||
rzutil.sanitize(false, Shared.evars);
|
||||
} catch (e) {
|
||||
errors.push(rzutil.debug(Shared.evars, e));
|
||||
}
|
||||
|
||||
if (!Shared.printer) {
|
||||
Shared.printer = new Printer();
|
||||
}
|
||||
Shared.printer.flushOutput(lines, errors, log);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import libdec from './libdec/libdec.js';
|
||||
import Printer from './libdec/printer.js';
|
||||
import TestSuite from './libdec/testsuite.js';
|
||||
|
||||
function main() {
|
||||
try {
|
||||
const test = new TestSuite();
|
||||
var Shared = Global();
|
||||
Shared.evars = test.evars;
|
||||
Shared.argdb = test.data.argdb;
|
||||
Shared.printer = new Printer();
|
||||
Shared.context = new libdec.context();
|
||||
var architecture = libdec.archs[Shared.evars.arch];
|
||||
// af seems to break renaming.
|
||||
/* asm.pseudo breaks things.. */
|
||||
if (test.data.graph && test.data.graph.length > 0) {
|
||||
var p = new libdec.core.session(test.data, architecture, Shared.evars);
|
||||
var arch_context = architecture.context(test.data);
|
||||
libdec.core.analysis.pre(p, architecture, arch_context);
|
||||
libdec.core.decompile(p, architecture, arch_context);
|
||||
libdec.core.analysis.post(p, architecture, arch_context);
|
||||
libdec.core.print(p);
|
||||
Shared.printer.flushOutput(Shared.context.lines, Shared.context.errors, Shared.context.log, Shared.evars.extra);
|
||||
} else {
|
||||
console.log('Error: no data available.\nPlease analyze the function/binary first.');
|
||||
}
|
||||
} catch (e) {
|
||||
const error = 'Exception: ' + e.message + ' (' + e.name + ')\n' + e.stack;
|
||||
const filename = unit.file.split('/').slice(-1)[0];
|
||||
console.log('File:', filename);
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,331 @@
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Long from './long.js';
|
||||
|
||||
var json_parse = function(options) {
|
||||
"use strict";
|
||||
// This is a function that can parse a JSON text, producing a JavaScript
|
||||
// data structure. It is a simple, recursive descent parser. It does not use
|
||||
// eval or regular expressions, so it can be used as a model for implementing
|
||||
// a JSON parser in other languages.
|
||||
// We are defining the function inside of another function to avoid creating
|
||||
// global variables.
|
||||
var at, // The index of the current character
|
||||
ch, // The current character
|
||||
escapee = {
|
||||
'"': '"',
|
||||
'\\': '\\',
|
||||
'/': '/',
|
||||
b: '\b',
|
||||
f: '\f',
|
||||
n: '\n',
|
||||
r: '\r',
|
||||
t: '\t'
|
||||
},
|
||||
text,
|
||||
error = function(m) {
|
||||
// Call error when something is wrong.
|
||||
throw {
|
||||
name: 'SyntaxError',
|
||||
message: m,
|
||||
at: at,
|
||||
text: text
|
||||
};
|
||||
},
|
||||
next = function(c) {
|
||||
// If a c parameter is provided, verify that it matches the current character.
|
||||
if (c && c !== ch) {
|
||||
error("Expected '" + c + "' instead of '" + ch + "'");
|
||||
}
|
||||
// Get the next character. When there are no more characters,
|
||||
// return the empty string.
|
||||
ch = text.charAt(at);
|
||||
at += 1;
|
||||
return ch;
|
||||
},
|
||||
number = function() {
|
||||
// Parse a number value.
|
||||
var number,
|
||||
string = '';
|
||||
if (ch === '-') {
|
||||
string = '-';
|
||||
next('-');
|
||||
}
|
||||
while (ch >= '0' && ch <= '9') {
|
||||
string += ch;
|
||||
next();
|
||||
}
|
||||
if (ch === '.') {
|
||||
string += '.';
|
||||
while (next() && ch >= '0' && ch <= '9') {
|
||||
string += ch;
|
||||
}
|
||||
}
|
||||
if (ch === 'e' || ch === 'E') {
|
||||
string += ch;
|
||||
next();
|
||||
if (ch === '-' || ch === '+') {
|
||||
string += ch;
|
||||
next();
|
||||
}
|
||||
while (ch >= '0' && ch <= '9') {
|
||||
string += ch;
|
||||
next();
|
||||
}
|
||||
}
|
||||
number = +string;
|
||||
if (!isFinite(number)) {
|
||||
error("Bad number");
|
||||
} else {
|
||||
if (string.indexOf('.') < 0) {
|
||||
try {
|
||||
return Long.from(string, true);
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
console.log(e.stack);
|
||||
return string;
|
||||
}
|
||||
}
|
||||
return number;
|
||||
}
|
||||
},
|
||||
string = function() {
|
||||
// Parse a string value.
|
||||
var hex,
|
||||
i,
|
||||
string = '',
|
||||
uffff;
|
||||
// When parsing for string values, we must look for " and \ characters.
|
||||
if (ch === '"') {
|
||||
while (next()) {
|
||||
if (ch === '"') {
|
||||
next();
|
||||
return string;
|
||||
}
|
||||
if (ch === '\\') {
|
||||
next();
|
||||
if (ch === 'u') {
|
||||
uffff = 0;
|
||||
for (i = 0; i < 4; i += 1) {
|
||||
hex = parseInt(next(), 16);
|
||||
if (!isFinite(hex)) {
|
||||
break;
|
||||
}
|
||||
uffff = uffff * 16 + hex;
|
||||
}
|
||||
string += String.fromCharCode(uffff);
|
||||
} else if (typeof escapee[ch] === 'string') {
|
||||
string += escapee[ch];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
string += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
error("Bad string");
|
||||
},
|
||||
white = function() {
|
||||
// Skip whitespace.
|
||||
while (ch && ch <= ' ') {
|
||||
next();
|
||||
}
|
||||
},
|
||||
word = function() {
|
||||
// true, false, or null.
|
||||
switch (ch) {
|
||||
case 't':
|
||||
next('t');
|
||||
next('r');
|
||||
next('u');
|
||||
next('e');
|
||||
return true;
|
||||
case 'f':
|
||||
next('f');
|
||||
next('a');
|
||||
next('l');
|
||||
next('s');
|
||||
next('e');
|
||||
return false;
|
||||
case 'n':
|
||||
next('n');
|
||||
next('u');
|
||||
next('l');
|
||||
next('l');
|
||||
return null;
|
||||
}
|
||||
error("Unexpected '" + ch + "'");
|
||||
},
|
||||
value, // Place holder for the value function.
|
||||
array = function() {
|
||||
// Parse an array value.
|
||||
var array = [];
|
||||
if (ch === '[') {
|
||||
next('[');
|
||||
white();
|
||||
if (ch === ']') {
|
||||
next(']');
|
||||
return array; // empty array
|
||||
}
|
||||
while (ch) {
|
||||
array.push(value());
|
||||
white();
|
||||
if (ch === ']') {
|
||||
next(']');
|
||||
return array;
|
||||
}
|
||||
next(',');
|
||||
white();
|
||||
}
|
||||
}
|
||||
error("Bad array");
|
||||
},
|
||||
object = function() {
|
||||
// Parse an object value.
|
||||
var key,
|
||||
object = {};
|
||||
if (ch === '{') {
|
||||
next('{');
|
||||
white();
|
||||
if (ch === '}') {
|
||||
next('}');
|
||||
return object; // empty object
|
||||
}
|
||||
while (ch) {
|
||||
key = string();
|
||||
white();
|
||||
next(':');
|
||||
object[key] = value();
|
||||
white();
|
||||
if (ch === '}') {
|
||||
next('}');
|
||||
return object;
|
||||
}
|
||||
next(',');
|
||||
white();
|
||||
}
|
||||
}
|
||||
error("Bad object");
|
||||
};
|
||||
value = function() {
|
||||
// Parse a JSON value. It could be an object, an array, a string, a number,
|
||||
// or a word.
|
||||
white();
|
||||
switch (ch) {
|
||||
case '{':
|
||||
return object();
|
||||
case '[':
|
||||
return array();
|
||||
case '"':
|
||||
return string();
|
||||
case '-':
|
||||
return number();
|
||||
default:
|
||||
return ch >= '0' && ch <= '9' ? number() : word();
|
||||
}
|
||||
};
|
||||
// Return the json_parse function. It will have access to all of the above
|
||||
// functions and variables.
|
||||
return function(source, reviver) {
|
||||
var result;
|
||||
text = source + '';
|
||||
at = 0;
|
||||
ch = ' ';
|
||||
result = value();
|
||||
white();
|
||||
if (ch) {
|
||||
error("Syntax error");
|
||||
}
|
||||
// If there is a reviver function, we recursively walk the new structure,
|
||||
// passing each name/value pair to the reviver function for possible
|
||||
// transformation, starting with a temporary root object that holds the result
|
||||
// in an empty key. If there is not a reviver function, we simply return the
|
||||
// result.
|
||||
return typeof reviver === 'function' ? (function walk(holder, key) {
|
||||
var v, value = holder[key];
|
||||
if (value && typeof value === 'object') {
|
||||
Object.keys(value).forEach(function(k) {
|
||||
v = walk(value, k);
|
||||
if (v !== undefined) {
|
||||
value[k] = v;
|
||||
} else {
|
||||
delete value[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
return reviver.call(holder, key, value);
|
||||
}({
|
||||
'': result
|
||||
}, '')) : result;
|
||||
};
|
||||
};
|
||||
|
||||
function _iterate_value(variable, pad, depth, used) {
|
||||
if (used.indexOf(variable) >= 0) {
|
||||
return null;
|
||||
}
|
||||
var spad = pad.repeat(depth);
|
||||
var nl = pad.length > 0 ? "\n" : "";
|
||||
var sp = "\"";
|
||||
var k, sval, str = "";
|
||||
var comma = false;
|
||||
if (Array.isArray(variable)) {
|
||||
used.push(variable);
|
||||
if (variable.length < 1) {
|
||||
return "[]";
|
||||
}
|
||||
str = "[" + nl;
|
||||
for (k = 0; k < variable.length; k++) {
|
||||
sval = _iterate_value(variable[k], pad, depth + 1, used);
|
||||
if (sval) {
|
||||
comma = true;
|
||||
str += spad + sval + "," + nl;
|
||||
}
|
||||
}
|
||||
if (comma) {
|
||||
str = str.slice(0, 0 - ("," + nl).length) + nl;
|
||||
str += pad.repeat(depth - 1) + "]";
|
||||
} else {
|
||||
str = str.slice(0, -1) + "]";
|
||||
}
|
||||
return str;
|
||||
} else if (["number", "bigint"].indexOf(typeof variable) >= 0) {
|
||||
return variable.toString(10);
|
||||
} else if (Long.isLong(variable)) {
|
||||
return variable.toString(10);
|
||||
} else if (typeof variable == "function") {
|
||||
return null;
|
||||
} else if (typeof variable == "object") {
|
||||
used.push(variable);
|
||||
str = "{" + nl;
|
||||
for (k in variable) {
|
||||
sval = _iterate_value(variable[k], pad, depth + 1, used);
|
||||
if (sval) {
|
||||
comma = true;
|
||||
str += spad + sp + k + sp + ":" + sval + "," + nl;
|
||||
}
|
||||
}
|
||||
if (comma) {
|
||||
str = str.slice(0, 0 - ("," + nl).length) + nl;
|
||||
str += pad.repeat(depth - 1) + "}";
|
||||
} else {
|
||||
str = str.slice(0, -1) + "}";
|
||||
}
|
||||
return str;
|
||||
} else {
|
||||
//console.log(typeof variable)
|
||||
return JSON.stringify(variable);
|
||||
}
|
||||
}
|
||||
|
||||
function stringify(variable, pad) {
|
||||
pad = pad ? pad : "";
|
||||
return _iterate_value(variable, pad, 1, []);
|
||||
}
|
||||
|
||||
export default {
|
||||
parse: json_parse(),
|
||||
stringify: stringify
|
||||
};
|
||||
@@ -0,0 +1,183 @@
|
||||
// SPDX-FileCopyrightText: 2020-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import rzutil from './rzutil.js';
|
||||
import Strings from './core/strings.js';
|
||||
import Functions from './core/functions.js';
|
||||
import Extra from './core/extra.js';
|
||||
import Long from './long.js';
|
||||
|
||||
const _re_keywords = /\bif\b|\belse\b|\bwhile\b|\bfor\b|\bdo\b|\breturn\b|\bthrow\b|\bnew\b|\b__asm\b/g;
|
||||
const _re_types = /\b[ui]+nt[123468]+_t\b|\bvoid\b|\bconst\b|\bsizeof\b|\bfloat\b|\bdouble\b|\bchar\b|\bwchar_t\b|\bextern\b|\bstruct\b|\bsize_t\b|\btime_t\b|\bboolean\b/g;
|
||||
const _re_numbers = /\b0x[0-9a-fA-F]+\b|\b\d+\b/g;
|
||||
const _re_string = /"[^"]+"/;
|
||||
const _re_objcall = /[\w.]+\s?\([^)]+\)|[\w.]+\s?\(\)/;
|
||||
|
||||
function Annotation(start, length, value, pointer, type, syntax_type) {
|
||||
this.start = start;
|
||||
this.end = start + length;
|
||||
this.type = type;
|
||||
if (["function_name", "function_parameter", "local_variable"].indexOf(this.type) >= 0) {
|
||||
this.name = value;
|
||||
}
|
||||
if (["function_name", "offset", "global_variable", "constant_variable"].indexOf(this.type) >= 0) {
|
||||
this.offset = pointer.toString();
|
||||
}
|
||||
if (this.type == "syntax_highlight") {
|
||||
this.syntax_highlight = syntax_type;
|
||||
}
|
||||
}
|
||||
|
||||
function _sort_by_start(x, y) {
|
||||
if (x.start == y.start) {
|
||||
if (x.type == "offset") {
|
||||
return -1;
|
||||
} else if (x.type == "syntax_highlight") {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return x.start - y.start;
|
||||
}
|
||||
|
||||
function _match_annotation(regex, line, callback) {
|
||||
var tokens, matches;
|
||||
matches = line.match(regex);
|
||||
tokens = line.split(regex);
|
||||
if (!matches) {
|
||||
return;
|
||||
}
|
||||
for (var i = 0, pos = 0; i < matches.length; i++) {
|
||||
pos += tokens[i].length;
|
||||
callback(pos, matches[i]);
|
||||
pos += matches[i].length;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
context: function() {
|
||||
var data = new rzutil.data(false);
|
||||
return {
|
||||
comment: false,
|
||||
arch: data.arch,
|
||||
strings: new Strings(data.xrefs.strings, true).data,
|
||||
functions: new Functions(data.xrefs.functions, true).data.map(function(x) {
|
||||
return {
|
||||
location: x.offset,
|
||||
value: Extra.replace.call(x.name),
|
||||
name: x.name
|
||||
};
|
||||
}),
|
||||
variables: data.arch == "dalvik" ? [] : Array.prototype.concat(data.xrefs.arguments.stack, data.xrefs.arguments.reg)
|
||||
};
|
||||
},
|
||||
parse: function(current, line, context) {
|
||||
var pos, old, i, a = [],
|
||||
it, tmp, ll = line.str.length;
|
||||
var string_beg = ll,
|
||||
string_end = -1;
|
||||
a.push(new Annotation(current, ll, null, line.offset, "offset"));
|
||||
if (line.str.trim().startsWith('/*') || context.comment) {
|
||||
context.comment = !line.str.endsWith('*/');
|
||||
a.push(new Annotation(current, ll, line.str, null, "syntax_highlight", "comment"));
|
||||
return a;
|
||||
} else if (line.str.startsWith('//')) {
|
||||
a.push(new Annotation(current, ll, line.str, null, "syntax_highlight", "comment"));
|
||||
return a;
|
||||
} else if (line.str.startsWith('#')) {
|
||||
a.push(new Annotation(current, ll, line.str, null, "syntax_highlight", "comment"));
|
||||
return a;
|
||||
}
|
||||
/* strings */
|
||||
for (i = context.strings.length - 1; i >= 0; i--) {
|
||||
it = context.strings[i];
|
||||
var str = '"' + it.value + '"';
|
||||
pos = line.str.indexOf(str);
|
||||
if (pos > 0) {
|
||||
// include the ""
|
||||
a.push(new Annotation(current + pos, str.length, str, it.location, "global_variable", null));
|
||||
a.push(new Annotation(current + pos, str.length, str, null, "syntax_highlight", "global_variable"));
|
||||
string_beg = pos;
|
||||
string_end = string_beg + str.length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* strings with regex */
|
||||
if (string_end < 0) {
|
||||
tmp = line.str.match(_re_string) || [];
|
||||
for (i = tmp.length - 1, old = 0; i >= 0; i--) {
|
||||
pos = line.str.indexOf(tmp[i]);
|
||||
a.push(new Annotation(current + pos, tmp[i].length, tmp[i], null, "syntax_highlight", "global_variable"));
|
||||
string_beg = pos;
|
||||
string_end = pos + tmp[i].length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* function names */
|
||||
if (context.arch == "dalvik") {
|
||||
pos = line.str.search(_re_objcall);
|
||||
if (pos >= 0 && !(pos >= string_beg && pos <= string_end)) {
|
||||
var callname = line.str.substr(pos, line.str.substr(pos).indexOf('(')).trim();
|
||||
if (['__asm'].indexOf(callname) < 0) {
|
||||
a.push(new Annotation(current + pos, callname.length, callname, null, "syntax_highlight", "function_name"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = context.functions.length - 1; i >= 0; i--) {
|
||||
it = context.functions[i];
|
||||
pos = line.str.search(new RegExp('\\b' + it.value + '\\b'));
|
||||
if (pos >= 0 && !(pos >= string_beg && pos <= string_end)) {
|
||||
a.push(new Annotation(current + pos, it.value.length, it.name, it.location, "function_name", null));
|
||||
a.push(new Annotation(current + pos, it.value.length, it.value, null, "syntax_highlight", "function_name"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* variables */
|
||||
for (i = context.variables.length - 1, old = 0; i >= 0; i--) {
|
||||
it = context.variables[i];
|
||||
if (!it) {
|
||||
continue;
|
||||
}
|
||||
pos = it.type.match(_re_types) ? -1 : line.str.search(new RegExp('\\b' + it.type + '\\b'));
|
||||
if (pos >= 0 && !(pos >= string_beg && pos <= string_end)) {
|
||||
a.push(new Annotation(current + pos, it.type.length, it.type, null, "syntax_highlight", "datatype"));
|
||||
}
|
||||
pos = line.str.search(new RegExp('\\b' + it.name + '\\b'));
|
||||
if (pos >= 0 && !(pos >= string_beg && pos <= string_end)) {
|
||||
var type = (it.kind === 'arg' || it.kind === 'reg') ? "function_parameter" : "local_variable";
|
||||
a.push(new Annotation(current + pos, it.name.length, it.name, line.offset, type, null));
|
||||
a.push(new Annotation(current + pos, it.name.length, it.name, null, "syntax_highlight", type));
|
||||
}
|
||||
}
|
||||
/* keywords */
|
||||
_match_annotation(_re_keywords, line.str, function(pos, match) {
|
||||
if (pos >= 0 && !(pos >= string_beg && pos <= string_end)) {
|
||||
a.push(new Annotation(current + pos, match.length, match, null, "syntax_highlight", "keyword"));
|
||||
}
|
||||
});
|
||||
/* numbers */
|
||||
_match_annotation(_re_numbers, line.str, function(pos, match) {
|
||||
if (match.length > 1 && match.charAt(0) == '0' && match.charAt(1) != 'x') {
|
||||
return;
|
||||
}
|
||||
if (match != '0') {
|
||||
pos = old + line.str.substr(old, ll).indexOf(match);
|
||||
} else {
|
||||
pos = old + line.str.substr(old, ll).search(/\b0\b/);
|
||||
}
|
||||
if (pos >= 0 && !(pos >= string_beg && pos <= string_end)) {
|
||||
var location = Long.from(match);
|
||||
a.push(new Annotation(current + pos, match.length, match, location, "constant_variable", null));
|
||||
a.push(new Annotation(current + pos, match.length, match, null, "syntax_highlight", "constant_variable"));
|
||||
}
|
||||
});
|
||||
/* types */
|
||||
_match_annotation(_re_types, line.str, function(pos, match) {
|
||||
if (pos >= 0 && !(pos >= string_beg && pos <= string_end)) {
|
||||
a.push(new Annotation(current + pos, match.length, match, null, "syntax_highlight", "datatype"));
|
||||
}
|
||||
});
|
||||
return a.sort(_sort_by_start);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,387 @@
|
||||
// SPDX-FileCopyrightText: 2019-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
|
||||
const _REG = {
|
||||
X: 'x',
|
||||
Y: 'y',
|
||||
stackptr: 'SP',
|
||||
accumulator: 'acc',
|
||||
carry: 'C',
|
||||
overflow: 'V',
|
||||
decimal: 'D',
|
||||
interrupt: 'I',
|
||||
zero: 'Z',
|
||||
};
|
||||
|
||||
function _arg(instr, idx) {
|
||||
var o = instr.parsed.opd;
|
||||
if (idx > 0) {
|
||||
return o[idx];
|
||||
}
|
||||
o = o[0];
|
||||
if (o.immediate) {
|
||||
return o.token;
|
||||
}
|
||||
return Variable.pointer(o.token, 16, false);
|
||||
}
|
||||
|
||||
function _math(op, instr, bits) {
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
if (!srcB) {
|
||||
return op(_REG.accumulator, _REG.accumulator, srcA, bits);
|
||||
}
|
||||
return Base.composed([
|
||||
op(_REG.accumulator, _REG.accumulator, srcA, bits),
|
||||
Base.add(_REG.accumulator, _REG.accumulator, srcB),
|
||||
]);
|
||||
}
|
||||
|
||||
function _math_rotate(op, instr, bits) {
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
if (!srcB) {
|
||||
// destination, source_a, source_b, bits
|
||||
return op(_REG.accumulator, srcA, bits, 16);
|
||||
}
|
||||
return Base.composed([
|
||||
op(_REG.accumulator, srcA, bits, 16),
|
||||
Base.add(_REG.accumulator, _REG.accumulator, srcB),
|
||||
]);
|
||||
}
|
||||
|
||||
function _load(instr, register, bits) {
|
||||
bits = bits || 16;
|
||||
register = register || _REG.accumulator;
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
var ops = [];
|
||||
if (typeof srcA == "string") {
|
||||
// pointer, register, bits, is_signed
|
||||
ops.push(Base.read_memory(register, register, srcA, bits));
|
||||
} else {
|
||||
ops.push(Base.assign(register, srcA));
|
||||
}
|
||||
if (!srcB) {
|
||||
return ops[0];
|
||||
}
|
||||
ops.push(Base.add(register, register, srcB));
|
||||
return Base.composed(ops);
|
||||
}
|
||||
|
||||
function _store(instr, register, bits) {
|
||||
bits = bits || 16;
|
||||
register = register || _REG.accumulator;
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
var ops = [];
|
||||
if (typeof srcA == "string") {
|
||||
// pointer, register, bits, is_signed
|
||||
ops.push(Base.write_memory(register, register, srcA, bits));
|
||||
} else {
|
||||
ops.push(Base.assign(srcA, register));
|
||||
}
|
||||
if (!srcB) {
|
||||
return ops[0];
|
||||
}
|
||||
ops.push(Base.add(register, register, srcB));
|
||||
return Base.composed(ops);
|
||||
}
|
||||
|
||||
function _nop(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
export default {
|
||||
instructions: {
|
||||
adc: function(instr) {
|
||||
return _math(Base.add, instr);
|
||||
},
|
||||
and: function(instr) {
|
||||
return _math(Base.and, instr);
|
||||
},
|
||||
asl: function(instr) {
|
||||
return _math(Base.shift_left, instr, 1);
|
||||
},
|
||||
bit: function(instr, context) {
|
||||
var p = _arg(instr, 0);
|
||||
context.cond.a = '(' + _REG.accumulator + ' & ' + p + ')';
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
bcc: function(instr, context) {
|
||||
instr.conditional(_REG.carry, 0, 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
bcs: function(instr, context) {
|
||||
instr.conditional(_REG.carry, 0, 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
beq: function(instr, context) {
|
||||
instr.conditional(context.cond.a, context.cond.b, 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
bmi: function(instr, context) {
|
||||
instr.conditional(context.cond.a, context.cond.b, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
bne: function(instr, context) {
|
||||
instr.conditional(context.cond.a, context.cond.b, 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
bpl: function(instr, context) {
|
||||
instr.conditional(context.cond.a, context.cond.b, 'GT');
|
||||
return Base.nop();
|
||||
},
|
||||
brk: function(instr, context) {
|
||||
Global().context.addMacro('#define INTERRUPT() __asm(brk)');
|
||||
return Base.call(Variable.macro('INTERRUPT'), []);
|
||||
},
|
||||
bvc: function(instr, context) {
|
||||
instr.conditional(_REG.overflow, 0, 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
bvs: function(instr, context) {
|
||||
instr.conditional(_REG.overflow, 0, 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
clc: function(instr, context) {
|
||||
return Base.assign(_REG.carry, '0');
|
||||
},
|
||||
cld: function(instr, context) {
|
||||
return Base.assign(_REG.decimal, '0');
|
||||
},
|
||||
cli: function(instr, context) {
|
||||
return Base.assign(_REG.interrupt, '0');
|
||||
},
|
||||
clv: function(instr, context) {
|
||||
return Base.assign(_REG.overflow, '0');
|
||||
},
|
||||
cmp: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _REG.accumulator;
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
if (!srcB) {
|
||||
context.cond.b = srcA;
|
||||
return Base.nop();
|
||||
}
|
||||
var v = Variable.uniqueName();
|
||||
context.cond.b = v;
|
||||
return Base.add(v, srcA, srcB);
|
||||
},
|
||||
cpx: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = _arg(instr, 0);
|
||||
return Base.nop();
|
||||
},
|
||||
cpy: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = _arg(instr, 0);
|
||||
return Base.nop();
|
||||
},
|
||||
dec: function(instr, context) {
|
||||
var p = _arg(instr, 0);
|
||||
context.cond.a = p;
|
||||
context.cond.b = '0';
|
||||
return Base.subtract(p, p, 1);
|
||||
},
|
||||
dex: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
return Base.subtract(_REG.X, _REG.X, 1);
|
||||
},
|
||||
dey: function(instr, context) {
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = '0';
|
||||
return Base.subtract(_REG.Y, _REG.Y, 1);
|
||||
},
|
||||
eor: function(instr, context) {
|
||||
return _math(Base.xor, instr);
|
||||
},
|
||||
inc: function(instr, context) {
|
||||
var p = _arg(instr, 0);
|
||||
context.cond.a = p;
|
||||
context.cond.b = '0';
|
||||
return Base.add(p, p, 1);
|
||||
},
|
||||
inx: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
return Base.add(_REG.X, _REG.X, 1);
|
||||
},
|
||||
iny: function(instr, context) {
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = '0';
|
||||
return Base.add(_REG.Y, _REG.Y, 1);
|
||||
},
|
||||
jmp: _nop,
|
||||
jsr: function(instr) {
|
||||
var p = instr.parsed.opd[0].token;
|
||||
if (p.startsWith('0x')) {
|
||||
p = Variable.functionPointer(p);
|
||||
}
|
||||
return Base.call(p);
|
||||
},
|
||||
lda: function(instr, context) {
|
||||
context.cond.a = _REG.accumulator;
|
||||
context.cond.b = '0';
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(_REG.accumulator, instr.parsed.opd[0].token.replace(/^sym\./, ''));
|
||||
}
|
||||
return _load(instr, _REG.accumulator, 16);
|
||||
},
|
||||
ldx: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(_REG.X, instr.parsed.opd[0].token.replace(/^sym\./, ''));
|
||||
}
|
||||
return _load(instr, _REG.X, 16);
|
||||
},
|
||||
ldy: function(instr, context) {
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = '0';
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(_REG.Y, instr.parsed.opd[0].token.replace(/^sym\./, ''));
|
||||
}
|
||||
return _load(instr, _REG.Y, 16);
|
||||
},
|
||||
lsr: function(instr) {
|
||||
return _math(Base.shift_right, instr, 1);
|
||||
},
|
||||
ora: function(instr, context) {
|
||||
return _math(Base.xor, instr);
|
||||
},
|
||||
pha: _nop,
|
||||
php: _nop,
|
||||
pla: _nop,
|
||||
plp: _nop,
|
||||
rol: function(instr) {
|
||||
return _math_rotate(Base.rotate_left, instr, 1);
|
||||
},
|
||||
ror: function(instr) {
|
||||
return _math_rotate(Base.rotate_right, instr, 1);
|
||||
},
|
||||
rti: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
rts: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
sbc: function(instr) {
|
||||
return _math(Base.subtract, instr);
|
||||
},
|
||||
sec: function(instr, context) {
|
||||
return Base.assign(_REG.carry, 1);
|
||||
},
|
||||
sed: function(instr, context) {
|
||||
return Base.assign(_REG.decimal, 1);
|
||||
},
|
||||
sei: function(instr, context) {
|
||||
return Base.assign(_REG.interrupt, 1);
|
||||
},
|
||||
sta: function(instr) {
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(instr.parsed.opd[0].token.replace(/^sym\./, ''), _REG.accumulator);
|
||||
}
|
||||
return _store(instr, _REG.accumulator, 16);
|
||||
},
|
||||
stx: function(instr) {
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(instr.parsed.opd[0].token.replace(/^sym\./, ''), _REG.X);
|
||||
}
|
||||
return _store(instr, _REG.X, 16);
|
||||
},
|
||||
sty: function(instr) {
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(instr.parsed.opd[0].token.replace(/^sym\./, ''), _REG.Y);
|
||||
}
|
||||
return _store(instr, _REG.Y, 16);
|
||||
},
|
||||
tax: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.X, _REG.accumulator);
|
||||
},
|
||||
tay: function(instr, context) {
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.Y, _REG.accumulator);
|
||||
},
|
||||
tsx: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.X, _REG.stackptr);
|
||||
},
|
||||
txs: function(instr, context) {
|
||||
return Base.assign(_REG.stackptr, _REG.X);
|
||||
},
|
||||
txa: function(instr, context) {
|
||||
context.cond.a = _REG.accumulator;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.accumulator, _REG.X);
|
||||
},
|
||||
tya: function(instr, context) {
|
||||
context.cond.a = _REG.accumulator;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.accumulator, _REG.Y);
|
||||
},
|
||||
nop: _nop,
|
||||
invalid: _nop
|
||||
},
|
||||
parse: function(assembly) {
|
||||
var tokens = assembly.trim().match(/^([\w]+) ?(#)?(\()?((0x[\da-fA-F]+)|([a-zA-Z0-9._]+))?(\))?(,([\w]+))?\)?$/);
|
||||
var opds = [];
|
||||
if (tokens[5] || tokens[6]) {
|
||||
var token = tokens[5] || tokens[6];
|
||||
if (tokens[3] == '(' && tokens[7] == ')' && tokens[9]) {
|
||||
token += ' + ' + tokens[9];
|
||||
}
|
||||
opds.push({
|
||||
immediate: tokens[2] == '#',
|
||||
pointer: tokens[3] == '(',
|
||||
token: token,
|
||||
});
|
||||
}
|
||||
if (tokens[9] && tokens[3] == '(' && tokens[7] != ')') {
|
||||
opds.push(tokens[9]);
|
||||
}
|
||||
return {
|
||||
mnem: tokens[1],
|
||||
opd: opds
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: _REG.accumulator,
|
||||
b: '?',
|
||||
}
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions, context) {},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,277 @@
|
||||
// SPDX-FileCopyrightText: 2021-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
|
||||
const EXTRAM = "eram + ";
|
||||
const INDRAM = "iram + ";
|
||||
|
||||
function resolve_opd(instr, index) {
|
||||
var a = instr.parsed.opd[index].replace(/\+/, ' + ');
|
||||
if (a.indexOf('#') == 0) {
|
||||
return a.substr(1, a.length);
|
||||
} else if (a.match(/^\d+$|^0x[a-fA-F\d]+$/)) {
|
||||
return Variable.pointer(INDRAM + a, 8, false);
|
||||
} else if (a.indexOf('@') == 0) {
|
||||
return Variable.pointer(EXTRAM + a.substr(1, a.length), 8, false);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function previous(instr, instructions) {
|
||||
return instructions[instructions.indexOf(instr) - 1];
|
||||
}
|
||||
|
||||
function is_mnem(instr, mnem, startswith) {
|
||||
if (startswith) {
|
||||
return instr && instr.parsed.mnem.indexOf(mnem) == 0;
|
||||
}
|
||||
return instr && instr.parsed.mnem == mnem;
|
||||
}
|
||||
|
||||
function is_last(instr, instructions) {
|
||||
return instructions.indexOf(instr) == (instructions.length - 1);
|
||||
}
|
||||
|
||||
function is_membit(instr, index) {
|
||||
return instr.parsed.opd[index].indexOf('.') > 0;
|
||||
}
|
||||
|
||||
function write_membit(instr, index, b) {
|
||||
var a = instr.parsed.opd[index].split('.');
|
||||
var p = Variable.pointer(a[0], 8, false);
|
||||
if (b == '1') {
|
||||
b = '0x' + (1 << parseInt(a[1])).toString(16);
|
||||
} else if (b != '0') {
|
||||
b = '(' + b + ' & 1)' + (parseInt(a[1]) > 0 ? ' << ' + a[1] : '');
|
||||
} else {
|
||||
var bit = parseInt(a[1]) < 1 ? '~1' : '~(1 << ' + a[1] + ')';
|
||||
return Base.and(p, p, bit);
|
||||
}
|
||||
return Base.or(p, p, b);
|
||||
}
|
||||
|
||||
function read_membit(instr, index, b) {
|
||||
var a = instr.parsed.opd[index].split('.');
|
||||
var p = Variable.pointer(a[0], 8, false);
|
||||
if (parseInt(a[1]) < 1) {
|
||||
return Base.and(b, p, '1');
|
||||
}
|
||||
return Base.assign(b, '(' + p.toString() + ' >> ' + a[1] + ') & 1');
|
||||
}
|
||||
|
||||
function _mov(instr, context, instructions) {
|
||||
if (is_membit(instr, 0)) {
|
||||
return write_membit(instr, 0, resolve_opd(instr, 1));
|
||||
} else if (is_membit(instr, 1)) {
|
||||
return read_membit(instr, 1, resolve_opd(instr, 0));
|
||||
}
|
||||
var a = resolve_opd(instr, 0);
|
||||
var b = resolve_opd(instr, 1);
|
||||
return Base.assign(a, b);
|
||||
}
|
||||
|
||||
function _call(instr, context, instructions) {
|
||||
var args = [];
|
||||
var prev = previous(instr, instructions);
|
||||
if (prev && prev.parsed.opd[0] == 'a') {
|
||||
args.push(prev.parsed.opd[0]);
|
||||
}
|
||||
return Base.call(instr.parsed.opd[0], args);
|
||||
}
|
||||
|
||||
function _jump(instr, context, instructions) {
|
||||
if (is_last(instr, instructions) &&
|
||||
(instr.jump.gt(instr.location) || instr.jump.lt(instructions[0].location))) {
|
||||
return _call(instr, context, instructions);
|
||||
}
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
function _math(instr, context, op, carry) {
|
||||
if (carry) {
|
||||
return op('a', op(resolve_opd(instr, 0), resolve_opd(instr, 1)), 'carry');
|
||||
}
|
||||
return op('a', resolve_opd(instr, 0), resolve_opd(instr, 1));
|
||||
}
|
||||
|
||||
function _nop(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
export default {
|
||||
instructions: {
|
||||
acall: _call,
|
||||
add: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.add);
|
||||
},
|
||||
addc: function(instr, context, instructions) {
|
||||
instr.comments.push("add with carry");
|
||||
return _math(instr, context, Base.add, true);
|
||||
},
|
||||
ajmp: _jump,
|
||||
anl: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.and);
|
||||
},
|
||||
cjne: function(instr, context, instructions) {
|
||||
instr.conditional(resolve_opd(instr, 0), resolve_opd(instr, 1), 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
clr: function(instr, context, instructions) {
|
||||
if (is_membit(instr, 0)) {
|
||||
return write_membit(instr, 0, '0');
|
||||
}
|
||||
return Base.assign(resolve_opd(instr, 0), '0');
|
||||
},
|
||||
cpl: function(instr, context, instructions) {
|
||||
return Base.not(instr.parsed.opd[0], instr.parsed.opd[0]);
|
||||
},
|
||||
dec: function(instr, context, instructions) {
|
||||
return Base.decrease(resolve_opd(instr, 0), '1');
|
||||
},
|
||||
div: function(instr, context, instructions) {
|
||||
return Base.divide('a', 'a', 'b');
|
||||
},
|
||||
djnz: function(instr, context, instructions) {
|
||||
var a = resolve_opd(instr, 0);
|
||||
instr.conditional(a, '0', 'NE');
|
||||
return Base.decrease(a, '1');
|
||||
},
|
||||
inc: function(instr, context, instructions) {
|
||||
return Base.increase(resolve_opd(instr, 0), '1');
|
||||
},
|
||||
jb: function(instr, context, instructions) {
|
||||
instr.conditional('b', '0', 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
jbc: function(instr, context, instructions) {
|
||||
instr.conditional('b', '0 && c', 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
jc: function(instr, context) {
|
||||
instr.conditional('c', '0', 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
jmp: _jump,
|
||||
jnb: function(instr, context, instructions) {
|
||||
instr.conditional('b', '0', 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
jnc: function(instr, context, instructions) {
|
||||
instr.conditional('c', '0', 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
jnz: function(instr, context, instructions) {
|
||||
var p = previous(instr, instructions);
|
||||
instr.conditional(is_mnem(p, 'mov', true) ? resolve_opd(p, 1) : 'a', '0', 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
jz: function(instr, context, instructions) {
|
||||
var p = previous(instr, instructions);
|
||||
instr.conditional(is_mnem(p, 'mov', true) ? resolve_opd(p, 1) : 'a', '0', 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
lcall: _call,
|
||||
ljmp: _jump,
|
||||
mov: _mov,
|
||||
movc: _mov,
|
||||
movx: _mov,
|
||||
mul: function(instr, context, instructions) {
|
||||
return Base.multiply('a', 'a', 'b');
|
||||
},
|
||||
orl: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.or);
|
||||
},
|
||||
pop: function(instr) {
|
||||
return Base.assign(resolve_opd(instr, 0), Base.call('pop', []));
|
||||
},
|
||||
push: function(instr) {
|
||||
return Base.call('push', [resolve_opd(instr, 0)]);
|
||||
},
|
||||
ret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
reti: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
rl: function(instr) {
|
||||
return Base.rotate_left('a', 'a', '1', 8);
|
||||
},
|
||||
rlc: function(instr) {
|
||||
instr.comments.push("rotate with carry");
|
||||
return Base.rotate_left('a', 'a', '1', 8);
|
||||
},
|
||||
rr: function(instr) {
|
||||
return Base.rotate_right('a', 'a', '1', 8);
|
||||
},
|
||||
rrc: function(instr) {
|
||||
instr.comments.push("rotate with carry");
|
||||
return Base.rotate_right('a', 'a', '1', 8);
|
||||
},
|
||||
setb: function(instr, context, instructions) {
|
||||
if (is_membit(instr, 0)) {
|
||||
return write_membit(instr, 0, '1');
|
||||
}
|
||||
return Base.assign(instr.parsed.opd[0], 1);
|
||||
},
|
||||
sjmp: _jump,
|
||||
subb: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.subtract);
|
||||
},
|
||||
swap: function(instr, context, instructions) {
|
||||
return Base.swap_endian(resolve_opd(instr, 1), resolve_opd(instr, 0), 8);
|
||||
},
|
||||
xch: function(instr, context, instructions) {
|
||||
return Base.swap(resolve_opd(instr, 0), resolve_opd(instr, 1), 8);
|
||||
},
|
||||
xchd: function(instr, context, instructions) {
|
||||
var a = resolve_opd(instr, 0);
|
||||
var b = resolve_opd(instr, 1);
|
||||
var tmp = Variable.uniqueName('tmp');
|
||||
var ops = [
|
||||
Base.and(tmp, a, '0xF'),
|
||||
Base.and(a, a, '0xF0'),
|
||||
Base.or(a, b, '0xF0'),
|
||||
Base.and(b, b, '0xF0'),
|
||||
Base.or(b, b, tmp),
|
||||
];
|
||||
context.local.push(tmp);
|
||||
return Base.composed(ops);
|
||||
},
|
||||
xrl: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.xor);
|
||||
},
|
||||
nop: _nop,
|
||||
reserved: _nop,
|
||||
invalid: _nop
|
||||
},
|
||||
parse: function(assembly) {
|
||||
assembly = assembly.trim().replace(/,/g, '');
|
||||
var tokens = assembly.split(' ');
|
||||
return {
|
||||
mnem: tokens[0],
|
||||
opd: tokens.splice(1)
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
local: []
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions, context) {},
|
||||
localvars: function(context) {
|
||||
return context.local.map(function(x) {
|
||||
return 'uint8_t ' + x;
|
||||
});
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,673 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
|
||||
const AVR_MEM_BITS = 16;
|
||||
const AVR_X_MACRONAME = 'MEM_X';
|
||||
const AVR_Y_MACRONAME = 'MEM_Y';
|
||||
const AVR_Z_MACRONAME = 'MEM_Z';
|
||||
const AVR_X_MACRO = '#define ' + AVR_X_MACRONAME + ' ((uint16_t*)((r27 << 8) | r26))';
|
||||
const AVR_Y_MACRO = '#define ' + AVR_Y_MACRONAME + ' ((uint16_t*)((r29 << 8) | r28))';
|
||||
const AVR_Z_MACRO = '#define ' + AVR_Z_MACRONAME + ' ((uint16_t*)((r31 << 8) | r30))';
|
||||
const AVR_MEMORY = {
|
||||
'x': {
|
||||
name: AVR_X_MACRONAME,
|
||||
macro: AVR_X_MACRO,
|
||||
high: 'r27',
|
||||
low: 'r26'
|
||||
},
|
||||
'y': {
|
||||
name: AVR_Y_MACRONAME,
|
||||
macro: AVR_Y_MACRO,
|
||||
high: 'r29',
|
||||
low: 'r28'
|
||||
},
|
||||
'z': {
|
||||
name: AVR_Z_MACRONAME,
|
||||
macro: AVR_Z_MACRO,
|
||||
high: 'r31',
|
||||
low: 'r30'
|
||||
}
|
||||
};
|
||||
var AVR_CTR = 0;
|
||||
|
||||
var _to_16bit = function(high, low) {
|
||||
if ((high == '0x00' || high == '0') && (low == '0x00' || low == '0')) {
|
||||
return '0';
|
||||
} else if (low == '0x00' || low == '0') {
|
||||
return '(' + high + ' << 8)';
|
||||
} else if (high == '0x00' || high == '0') {
|
||||
return low;
|
||||
}
|
||||
return '(' + low + ' | (' + high + ' << 8))';
|
||||
};
|
||||
|
||||
var _common_math = function(instr, op) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed.opd;
|
||||
return op(e[0], e[0], e[1]);
|
||||
};
|
||||
|
||||
var _compare = function(a, b, context) {
|
||||
context.cond.a = a;
|
||||
context.cond.b = b;
|
||||
};
|
||||
|
||||
var _compare_values = function(a1, b1, a2, b2, context) {
|
||||
context.cond.a = _to_16bit(a2, a1);
|
||||
context.cond.b = _to_16bit(b2, b1);
|
||||
};
|
||||
|
||||
var _compare_bytes = function(a, b, context) {
|
||||
var a1 = context.cond.a;
|
||||
var b1 = context.cond.b;
|
||||
_compare_values(a1, b1, a, b, context);
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, type) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
if (context.cond.instr) {
|
||||
context.cond.instr.valid = false;
|
||||
}
|
||||
};
|
||||
|
||||
var _next_register = function(reg) {
|
||||
var num = parseInt(reg.substr(1, reg.length)) + 1;
|
||||
return 'r' + num;
|
||||
};
|
||||
|
||||
var _returns_r0 = function(instr, context, inverted) {
|
||||
if (inverted && instr.parsed.opd[1] == 'r0') {
|
||||
context.returns = 'r0';
|
||||
} else if (!inverted && instr.parsed.opd[0] == 'r0') {
|
||||
context.returns = 'r0';
|
||||
}
|
||||
};
|
||||
|
||||
var _conditional_next = function(instr, context, instructions, type) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
var next = instructions[instructions.indexOf(instr) + 1];
|
||||
if (next) {
|
||||
instr.jump = next.location;
|
||||
}
|
||||
if (context.cond.instr) {
|
||||
context.cond.instr.valid = false;
|
||||
}
|
||||
};
|
||||
|
||||
var _load16 = function(instr, context) {
|
||||
var v, m, op;
|
||||
_returns_r0(instr, context);
|
||||
var ptr = instr.parsed.opd[1];
|
||||
if (ptr.indexOf('-') >= 0) {
|
||||
ptr = ptr.replace('-', '');
|
||||
v = Variable.uniqueName('local');
|
||||
//pointer, register, bits, is_signed
|
||||
m = [
|
||||
Base.read_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[0], 8, false),
|
||||
Base.assign('uint8_t ' + v, AVR_MEMORY[ptr].low),
|
||||
Base.subtract(AVR_MEMORY[ptr].low, AVR_MEMORY[ptr].low, 1),
|
||||
Base.conditional_math(AVR_MEMORY[ptr].high, AVR_MEMORY[ptr].low, v, 'LT', AVR_MEMORY[ptr].high, 1, AVR_MEMORY[ptr].high, '-')
|
||||
];
|
||||
op = Base.composed(m);
|
||||
Global().context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
} else if (ptr.indexOf('+') >= 0) {
|
||||
ptr = ptr.replace('+', '');
|
||||
v = Variable.uniqueName('local');
|
||||
//pointer, register, bits, is_signed
|
||||
m = [
|
||||
Base.read_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[0], 8, false),
|
||||
Base.assign('uint8_t ' + v, AVR_MEMORY[ptr].low),
|
||||
Base.add(AVR_MEMORY[ptr].low, AVR_MEMORY[ptr].low, 1),
|
||||
Base.conditional_math(AVR_MEMORY[ptr].high, AVR_MEMORY[ptr].low, v, 'LT', AVR_MEMORY[ptr].high, 1, AVR_MEMORY[ptr].high, '+')
|
||||
];
|
||||
op = Base.composed(m);
|
||||
Global().context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
}
|
||||
//pointer, register, bits, is_signed
|
||||
op = Base.read_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[0], 8, false);
|
||||
Global().context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
};
|
||||
|
||||
var _store16 = function(instr, context) {
|
||||
var v, m, op;
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
var ptr = instr.parsed.opd[0];
|
||||
if (ptr.indexOf('-') >= 0) {
|
||||
ptr = ptr.replace(/-\d?/, '');
|
||||
v = Variable.uniqueName('local');
|
||||
//pointer, register, bits, is_signed
|
||||
m = [
|
||||
Base.write_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[1], 8, false),
|
||||
Base.assign('uint8_t ' + v, AVR_MEMORY[ptr].low),
|
||||
Base.subtract(AVR_MEMORY[ptr].low, AVR_MEMORY[ptr].low, 1),
|
||||
Base.conditional_math(AVR_MEMORY[ptr].high, AVR_MEMORY[ptr].low, v, 'LT', AVR_MEMORY[ptr].high, 1, AVR_MEMORY[ptr].high, '-')
|
||||
];
|
||||
op = Base.composed(m);
|
||||
Global().context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
} else if (ptr.indexOf('+') >= 0) {
|
||||
ptr = ptr.replace(/\+\d?/, '');
|
||||
v = Variable.uniqueName('local');
|
||||
//pointer, register, bits, is_signed
|
||||
m = [
|
||||
Base.write_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[1], 8, false),
|
||||
Base.assign('uint8_t ' + v, AVR_MEMORY[ptr].low),
|
||||
Base.add(AVR_MEMORY[ptr].low, AVR_MEMORY[ptr].low, 1),
|
||||
Base.conditional_math(AVR_MEMORY[ptr].high, AVR_MEMORY[ptr].low, v, 'LT', AVR_MEMORY[ptr].high, 1, AVR_MEMORY[ptr].high, '+')
|
||||
];
|
||||
op = Base.composed(m);
|
||||
Global().context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
}
|
||||
//pointer, register, bits, is_signed
|
||||
op = Base.write_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[1], 8, false);
|
||||
Global().context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
};
|
||||
|
||||
export default {
|
||||
instructions: {
|
||||
adc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.add);
|
||||
},
|
||||
add: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.add);
|
||||
},
|
||||
adiw: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
var b = instr.parsed.opd[1];
|
||||
var a1 = instr.parsed.opd[0];
|
||||
var a2 = _next_register(instr.parsed.opd[0]);
|
||||
_compare_values(a1, b, a2, b, context);
|
||||
context.cond.instr = instr;
|
||||
if (b == '0x00' || b == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var op1 = null;
|
||||
var op2 = null;
|
||||
if (b == '0x01' || b == '1') {
|
||||
op1 = Base.increase(a1, '1');
|
||||
op2 = Base.increase(a2, '1');
|
||||
} else {
|
||||
op1 = Base.add(a1, a1, b);
|
||||
op2 = Base.add(a2, a2, b);
|
||||
}
|
||||
return Base.composed([op1, op2]);
|
||||
},
|
||||
and: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.and);
|
||||
},
|
||||
andi: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.and);
|
||||
},
|
||||
asr: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.shift_right(instr.parsed.opd[0], instr.parsed.opd[0], '1');
|
||||
},
|
||||
brbc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var old_b = context.cond.b;
|
||||
context.cond.b = '0';
|
||||
_conditional(instr, context, 'NE');
|
||||
context.cond.b = old_b;
|
||||
return Base.nop();
|
||||
},
|
||||
brbs: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var old_b = context.cond.b;
|
||||
context.cond.b = '(1 << ' + instr.parsed.opd[0] + ')';
|
||||
_conditional(instr, context, 'NE');
|
||||
context.cond.b = old_b;
|
||||
return Base.nop();
|
||||
},
|
||||
brcc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'GE');
|
||||
return Base.nop();
|
||||
},
|
||||
brcs: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
breq: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
brlo: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
brlt: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
brmi: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
brne: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
brpl: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'GT');
|
||||
return Base.nop();
|
||||
},
|
||||
brsh: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LE');
|
||||
return Base.nop();
|
||||
},
|
||||
bset: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.macro('SREG_SET_BIT (' + instr.parsed.opd[0] + ')', '#define SREG_SET_BIT(x) __asm(bset (x))');
|
||||
},
|
||||
call: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
//name, args, is_pointer, returns, bits
|
||||
if (instr.parsed.opd[0].indexOf('0x') == 0) {
|
||||
return Base.call(Variable.functionPointer(instr.parsed.opd[0], 16), []);
|
||||
}
|
||||
return Base.call(instr.parsed.opd[0], []);
|
||||
},
|
||||
cli: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.macro('DISABLE_INTERRUPTS', '#define DISABLE_INTERRUPTS __asm(cli)');
|
||||
},
|
||||
clr: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.assign(instr.parsed.opd[0], '0');
|
||||
},
|
||||
clt: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.macro('CLEAR_TRANSFER_FLAG', '#define CLEAR_TRANSFER_FLAG __asm(clt)');
|
||||
},
|
||||
com: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.subtract(instr.parsed.opd[0], '0xff', instr.parsed.opd[0]);
|
||||
},
|
||||
cp: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return Base.nop();
|
||||
},
|
||||
cpc: function(instr, context, instructions) {
|
||||
instr.setBadJump();
|
||||
var p = instructions.indexOf(instr);
|
||||
if (instructions[p - 1] && instructions[p - 1].parsed.mnem.indexOf('cp') == 0) {
|
||||
_compare_bytes(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
} else {
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
}
|
||||
context.cond.instr = instr;
|
||||
return Base.nop();
|
||||
},
|
||||
cpi: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return Base.nop();
|
||||
},
|
||||
dec: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
_compare('--' + instr.parsed.opd[0], '0', context);
|
||||
context.cond.instr = instr;
|
||||
return Base.decrease(instr.parsed.opd[0], '1');
|
||||
},
|
||||
eor: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.xor);
|
||||
},
|
||||
icall: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
instr.setBadJump();
|
||||
//name, args, is_pointer, returns, bits
|
||||
var op = Base.call(Variable.functionPointer(AVR_MEMORY.z.name), []);
|
||||
Global().context.addMacro(AVR_MEMORY.z.macro);
|
||||
return op;
|
||||
},
|
||||
ijmp: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
instr.setBadJump();
|
||||
var op = Base.goto(AVR_MEMORY.z.name);
|
||||
Global().context.addMacro(AVR_MEMORY.z.macro);
|
||||
return op;
|
||||
},
|
||||
in: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var e = instr.parsed.opd;
|
||||
return Base.macro('READ_FROM_IO (' + e[1] + ', ' + e[0] + ')', '#define READ_FROM_IO(x,y) __asm(in (y), (x))');
|
||||
},
|
||||
inc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare('++' + instr.parsed.opd[0], '0', context);
|
||||
context.cond.instr = instr;
|
||||
return Base.increase(instr.parsed.opd[0], '1');
|
||||
},
|
||||
iret: function(instr) {
|
||||
return Base.macro('RETURN_FROM_INTERRUPT', '#define RETURN_FROM_INTERRUPT __asm(iret)');
|
||||
},
|
||||
jmp: function(instr, context) {
|
||||
return Base.nop();
|
||||
},
|
||||
ld: _load16,
|
||||
ldd: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var ptr = instr.parsed.opd[1].match(/([xyz]|[+-0-9]+)/g);
|
||||
var offset = ptr[1].replace(/(-)/, ' - ').replace(/(\+)/, ' + ');
|
||||
//pointer, register, bits, is_signed
|
||||
var op = Base.read_memory(AVR_MEMORY[ptr[0]].name + offset, instr.parsed.opd[0], 8, false);
|
||||
Global().context.addMacro(AVR_MEMORY[ptr[0]].macro);
|
||||
return op;
|
||||
},
|
||||
ldi: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.assign(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
lds: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(instr.parsed.opd[1], instr.parsed.opd[0], 8, false);
|
||||
},
|
||||
lpm: _load16,
|
||||
lsl: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.shift_left(instr.parsed.opd[0], instr.parsed.opd[0], '1');
|
||||
},
|
||||
lsr: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.shift_right(instr.parsed.opd[0], instr.parsed.opd[0], '1');
|
||||
},
|
||||
mov: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.assign(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
movw: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var a1 = instr.parsed.opd[0];
|
||||
var a2 = _next_register(instr.parsed.opd[0]);
|
||||
var b1 = instr.parsed.opd[1];
|
||||
var b2 = _next_register(instr.parsed.opd[1]);
|
||||
var op1 = Base.assign(a1, b1);
|
||||
var op2 = Base.assign(a2, b2);
|
||||
return Base.composed([op1, op2]);
|
||||
},
|
||||
mul: function(instr, context) {
|
||||
context.returns = 'r0';
|
||||
var name = 'value' + (++AVR_CTR);
|
||||
var ops = [];
|
||||
ops.push(Base.multiply('uint16_t ' + name, instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.assign('r0', '(' + name + ' & 0xFF)'));
|
||||
ops.push(Base.assign('r1', '(' + name + ' >> 8)'));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
muls: function(instr, context) {
|
||||
context.returns = 'r0';
|
||||
var name = 'value' + (++AVR_CTR);
|
||||
var ops = [];
|
||||
ops.push(Base.multiply('int16_t ' + name, instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.assign('r0', '(' + name + ' & 0xFF)'));
|
||||
ops.push(Base.assign('r1', '(' + name + ' >> 8)'));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
mulsu: function(instr, context) {
|
||||
context.returns = 'r0';
|
||||
var name = 'value' + (++AVR_CTR);
|
||||
var ops = [];
|
||||
ops.push(Base.multiply('uint16_t ' + name, instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.assign('r0', '(' + name + ' & 0xFF)'));
|
||||
ops.push(Base.assign('r1', '(' + name + ' >> 8)'));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
neg: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.negate(instr.parsed.opd[0], instr.parsed.opd[0]);
|
||||
},
|
||||
nop: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
},
|
||||
not: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.not(instr.parsed.opd[0], instr.parsed.opd[0]);
|
||||
},
|
||||
or: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.or);
|
||||
},
|
||||
ori: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.or);
|
||||
},
|
||||
out: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context, true);
|
||||
var e = instr.parsed.opd;
|
||||
return Base.macro('WRITE_TO_IO (' + e[0] + ', ' + e[1] + ')', '#define WRITE_TO_IO(x,y) __asm(in (x), (y))');
|
||||
},
|
||||
pop: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
if (instr.parsed.opd[0] == 'r0') {
|
||||
// if pops an r0, then the return value is void.
|
||||
context.returns = null;
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
push: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
},
|
||||
rcall: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.call(Variable.functionPointer(instr.parsed.opd[0], AVR_MEM_BITS), []);
|
||||
},
|
||||
ret: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
// returns r1:r0
|
||||
// but if pop r0 then return void.
|
||||
return Base.return(context.returns);
|
||||
},
|
||||
reti: function(instr) {
|
||||
return Base.macro('RETURN_FROM_INTERRUPT', '#define RETURN_FROM_INTERRUPT __asm(reti)');
|
||||
},
|
||||
rjmp: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.nop();
|
||||
},
|
||||
rol: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.rotate_left(instr.parsed.opd[0], instr.parsed.opd[0], '1', 8);
|
||||
},
|
||||
ror: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.rotate_right(instr.parsed.opd[0], instr.parsed.opd[0], '1', 8);
|
||||
},
|
||||
sbc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.subtract);
|
||||
},
|
||||
sbci: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.subtract);
|
||||
},
|
||||
sbiw: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
var b = instr.parsed.opd[1];
|
||||
var a1 = instr.parsed.opd[0];
|
||||
var a2 = _next_register(instr.parsed.opd[0]);
|
||||
_compare_values(a1, b, a2, b, context);
|
||||
context.cond.instr = instr;
|
||||
if (b == '0x00' || b == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var op1 = null;
|
||||
var op2 = null;
|
||||
if (b == '0x01' || b == '1') {
|
||||
op1 = Base.decrease(a1, '1');
|
||||
op2 = Base.decrease(a2, '1');
|
||||
} else {
|
||||
op1 = Base.subtract(a1, a1, b);
|
||||
op2 = Base.subtract(a2, a2, b);
|
||||
}
|
||||
return Base.composed([op1, op2]);
|
||||
},
|
||||
sbr: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.or(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
sbrc: function(instr, context, instructions) {
|
||||
instr.setBadJump();
|
||||
var next = instructions[instructions.indexOf(instr) + 1];
|
||||
if (next) {
|
||||
_compare('(' + instr.parsed.opd[0] + ' & (1 << ' + instr.parsed.opd[1] + '))', '0', context);
|
||||
context.cond.instr = instr;
|
||||
_conditional_next(next, context, instructions, 'EQ');
|
||||
return Base.or(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
sbrs: function(instr, context, instructions) {
|
||||
instr.setBadJump();
|
||||
var next = instructions[instructions.indexOf(instr) + 1];
|
||||
if (next) {
|
||||
_compare('(' + instr.parsed.opd[0] + ' & (1 << ' + instr.parsed.opd[1] + '))', '1', context);
|
||||
context.cond.instr = instr;
|
||||
_conditional_next(next, context, instructions, 'EQ');
|
||||
return Base.or(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
sei: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.macro('ENABLE_INTERRUPTS', '#define ENABLE_INTERRUPTS __asm(sei)');
|
||||
},
|
||||
ser: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.assign(instr.parsed.opd[0], '0xff');
|
||||
},
|
||||
set: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.macro('SET_TRANSFER_FLAG', '#define SET_TRANSFER_FLAG __asm(set)');
|
||||
},
|
||||
st: _store16,
|
||||
std: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
var ptr = instr.parsed.opd[0].match(/([xyz]|[+-0-9]+)/g);
|
||||
var offset = ptr[1].replace(/(-)/, ' - ').replace(/(\+)/, ' + ');
|
||||
//pointer, register, bits, is_signed
|
||||
var op = Base.write_memory(AVR_MEMORY[ptr[0]].name + offset, instr.parsed.opd[1], 8, false);
|
||||
Global().context.addMacro(AVR_MEMORY[ptr[0]].macro);
|
||||
return op;
|
||||
},
|
||||
sts: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(instr.parsed.opd[0], instr.parsed.opd[1], 8, false);
|
||||
},
|
||||
sub: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.subtract);
|
||||
},
|
||||
subi: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
if (instr.parsed.opd[1] == '0x00' || instr.parsed.opd[1] == '0') {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
}
|
||||
return _common_math(instr, Base.subtract);
|
||||
},
|
||||
tst: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_compare(instr.parsed.opd[0], '0', context);
|
||||
context.cond.instr = instr;
|
||||
},
|
||||
wdr: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
return Base.macro('CLEAR_WATCHDOG', '#define CLEAR_WATCHDOG __asm(wdr)');
|
||||
},
|
||||
invalid: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(asm) {
|
||||
asm = asm.toLowerCase();
|
||||
var ret = asm.replace(/\[|\]/g, ' ').replace(/,/g, ' ');
|
||||
ret = ret.replace(/\{|\}/g, ' ').replace(/\s+/g, ' ');
|
||||
ret = ret.trim().replace(/0x00/, '0').split(' ');
|
||||
|
||||
return {
|
||||
mnem: ret.shift(),
|
||||
opd: ret
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: null,
|
||||
b: null,
|
||||
instr: null
|
||||
},
|
||||
returns: null
|
||||
};
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return context.returns == null ? 'void' : 'uint8_t';
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,427 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
|
||||
var _to_size = function(value) {
|
||||
if (value == 'b') {
|
||||
return 8;
|
||||
} else if (value == 'w') {
|
||||
return 16;
|
||||
} else if (value == 'l') {
|
||||
return 32;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var _as_pointer = function(e, where, signed, ispointer) {
|
||||
var arg = e.opd[where];
|
||||
if (arg.pointer) {
|
||||
if (arg.presub) {
|
||||
var m = e.bits / 8;
|
||||
if (m == 1) {
|
||||
arg = Variable.pointer('--' + arg.pointer, e.bits, signed);
|
||||
} else {
|
||||
arg = Variable.pointer(arg.pointer + ' -= ' + m, e.bits, signed);
|
||||
}
|
||||
} else {
|
||||
return Variable.pointer(arg.pointer, e.bits, signed);
|
||||
}
|
||||
} else if (ispointer && arg.register.indexOf('0x') == 0) {
|
||||
var tokens = arg.register.replace(/\.([bwl])/, ' $1').split(' ');
|
||||
arg = Variable.pointer(tokens[0], _to_size(tokens[1]), signed);
|
||||
} else {
|
||||
return arg.register;
|
||||
}
|
||||
return arg;
|
||||
};
|
||||
|
||||
var _is_register = function(name) {
|
||||
return name && name.match(/\b[acdsACDS][0-9ixIX]\b/);
|
||||
};
|
||||
|
||||
var _common_math = function(e, op) {
|
||||
var src = _as_pointer(e, 0, true);
|
||||
var dst = _as_pointer(e, 1, true);
|
||||
if (e.opd[1].postadd) {
|
||||
return Base.composed([op(dst, dst, src), Base.add(e.opd[1].register, e.opd[1].register, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op(dst, dst, src);
|
||||
};
|
||||
|
||||
var _move = function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var src = _as_pointer(e, 0, true, true);
|
||||
var dst = _as_pointer(e, 1, true, true);
|
||||
return Base.assign(dst, src);
|
||||
};
|
||||
|
||||
var _move_multiple = function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var memory, pointer, postadd, register, op;
|
||||
var e = instr.parsed;
|
||||
var incr = (e.bits / 8).toString();
|
||||
var ops = [];
|
||||
if (e.opd[0].pointer) {
|
||||
//movem.l -(a7), d2-d3/a2-a5
|
||||
memory = _as_pointer(e, 0, false);
|
||||
pointer = e.opd[0].register;
|
||||
postadd = e.opd[0].postadd;
|
||||
register = e.opd[1].register;
|
||||
op = Base.read_memory;
|
||||
} else {
|
||||
//movem.l d2-d3/a2-a5, -(a7)
|
||||
memory = _as_pointer(e, 1, false);
|
||||
pointer = e.opd[1].register;
|
||||
postadd = e.opd[1].postadd;
|
||||
register = e.opd[0].register;
|
||||
op = Base.write_memory;
|
||||
}
|
||||
var reg = register.split('/');
|
||||
for (var i = 0; i < reg.length; i++) {
|
||||
var prefix = reg[i].charAt(0);
|
||||
var tmp = reg[i].replace(/[a-zA-Z]/g, '').split('-');
|
||||
var start = tmp[0];
|
||||
var end = tmp[1] || tmp[0];
|
||||
for (var j = start; j <= end; j++) {
|
||||
ops.push(op(memory, prefix + j, e.bits, false));
|
||||
if (postadd) {
|
||||
ops.push(Base.add(pointer, pointer, incr));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Base.composed(ops);
|
||||
};
|
||||
|
||||
var _compare = function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _as_pointer(instr.parsed, 1, false);
|
||||
context.cond.b = _as_pointer(instr.parsed, 0, false);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, type, zero) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _bitmask = function(offset, size) {
|
||||
var mask = 0;
|
||||
for (var i = 0; i < size; i++) {
|
||||
mask <<= 1;
|
||||
mask |= 1;
|
||||
}
|
||||
return '0x' + ((mask << offset) >>> 0).toString(16);
|
||||
};
|
||||
|
||||
export default {
|
||||
instructions: {
|
||||
add: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
addi: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
addq: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
and: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.and);
|
||||
},
|
||||
andi: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.and);
|
||||
},
|
||||
bftst: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var vals = instr.assembly.match(/(\w+)(?:{(\d+):(\d+)})/);
|
||||
var mask = _bitmask(vals[2], vals[3]);
|
||||
var test = Variable.local('(' + vals[1] + ' & ' + mask + ')');
|
||||
context.cond.a = test;
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
bne: function(instr, context) {
|
||||
return _conditional(instr, context, 'NE');
|
||||
},
|
||||
beq: function(instr, context) {
|
||||
return _conditional(instr, context, 'EQ');
|
||||
},
|
||||
bgt: function(instr, context) {
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bge: function(instr, context) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
blt: function(instr, context) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
ble: function(instr, context) {
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
bra: function(instr, context) {
|
||||
return Base.nop();
|
||||
},
|
||||
btst: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var and = instr.parsed.opd[0].register == '0x0' ? ' & 1)' : ' & (1 << ' + instr.parsed.opd[0].register + '))';
|
||||
var test = Variable.local('(' + instr.parsed.opd[1].register + and);
|
||||
context.cond.a = test;
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
clr: function(instr, context) {
|
||||
var e = instr.parsed;
|
||||
var dst = _as_pointer(e, 0, false);
|
||||
var op = Base.assign(dst, '0');
|
||||
if (e.opd[0].postadd) {
|
||||
var ptr_reg = e.opd[0].register;
|
||||
return Base.composed([op, Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
},
|
||||
cmp: _compare,
|
||||
cmpi: _compare,
|
||||
eori: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.xor);
|
||||
},
|
||||
jsr: function(instr) {
|
||||
var arg = _is_register(instr.parsed.opd[0].register) ? Variable.functionPointer(instr.parsed.opd[0].register) : instr.parsed.opd[0].register;
|
||||
return Base.call(arg, []);
|
||||
},
|
||||
lea: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var src = instr.string ? Variable.string(instr.string) : _as_pointer(instr.parsed, 0, false, true);
|
||||
var dst = _as_pointer(instr.parsed, 1, false, true);
|
||||
return Base.assign(dst, src);
|
||||
},
|
||||
link: function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var src = e.opd[0].register;
|
||||
var dst = e.opd[1].register;
|
||||
return Base.write_memory(dst == '0x0' ? 'a7' : ('a7 + ' + dst), src, e.bits, false);
|
||||
},
|
||||
lsl: function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var src = _as_pointer(e, 0, false);
|
||||
var dst = _as_pointer(e, 1, false);
|
||||
if (!e.opd[1]) {
|
||||
return Base.shift_left(dst, dst, '1');
|
||||
} else if (e.opd[1].pointer) {
|
||||
var op = Base.shift_left(dst, dst, src);
|
||||
if (e.opd[1].postadd) {
|
||||
var ptr_reg = e.opd[1].register;
|
||||
return Base.composed([op, Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
return Base.shift_left(dst, dst, src);
|
||||
|
||||
},
|
||||
lsr: function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var src = _as_pointer(e, 0, false);
|
||||
var dst = _as_pointer(e, 1, false);
|
||||
if (!e.opd[1]) {
|
||||
return Base.shift_right(dst, dst, '1');
|
||||
} else if (e.opd[1].pointer) {
|
||||
var op = Base.shift_right(dst, dst, src);
|
||||
if (e.opd[1].postadd) {
|
||||
var ptr_reg = e.opd[1].register;
|
||||
return Base.composed([op, Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
return Base.shift_right(dst, dst, src);
|
||||
},
|
||||
move: function(instr) {
|
||||
instr.setBadJump();
|
||||
var ptr_reg, ops;
|
||||
var e = instr.parsed;
|
||||
var pointer = _as_pointer(e, 0, false, true);
|
||||
var register = _as_pointer(e, 1, false, true);
|
||||
|
||||
if (e.opd[1].postadd) {
|
||||
ptr_reg = e.opd[1].register;
|
||||
ops = [
|
||||
Base.read_memory(pointer, register, e.bits, false),
|
||||
Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))
|
||||
];
|
||||
if (e.opd[0].postadd) {
|
||||
ptr_reg = e.opd[0].register;
|
||||
ops.push(Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8)));
|
||||
}
|
||||
return Base.composed(ops);
|
||||
} else if (e.opd[0].postadd) {
|
||||
ptr_reg = e.opd[0].register;
|
||||
ops = [
|
||||
Base.read_memory(pointer, register, e.bits, false),
|
||||
Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))
|
||||
];
|
||||
return Base.composed(ops);
|
||||
}
|
||||
return Base.read_memory(pointer, register, e.bits, false);
|
||||
},
|
||||
movea: _move,
|
||||
movem: _move_multiple,
|
||||
moveq: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
return Base.assign(instr.parsed.opd[1].register, instr.parsed.opd[0].register);
|
||||
},
|
||||
or: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.or);
|
||||
},
|
||||
ori: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.or);
|
||||
},
|
||||
pea: function(instr) {
|
||||
instr.setBadJump();
|
||||
var bits = instr.parsed.bits;
|
||||
var pointer = Variable.pointer('a7 -= 4', bits, false);
|
||||
var register = instr.string ? Variable.string(instr.string) : _as_pointer(instr.parsed, 0, false);
|
||||
return Base.write_memory(pointer, register, bits, false);
|
||||
},
|
||||
rts: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.return();
|
||||
},
|
||||
sf: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var dst = _as_pointer(e, 0, false, true);
|
||||
var op = Base.assign(dst, '0');
|
||||
if (e.opd[1].postadd) {
|
||||
return Base.composed([op, Base.add(e.opd[1].register, e.opd[1].register, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
},
|
||||
st: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var dst = _as_pointer(e, 0, false, true);
|
||||
var op = Base.assign(dst, '1');
|
||||
if (e.opd[0].postadd) {
|
||||
return Base.composed([op, Base.add(e.opd[0].register, e.opd[0].register, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
},
|
||||
sub: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.subtract);
|
||||
},
|
||||
subq: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.subtract);
|
||||
},
|
||||
tst: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _as_pointer(instr.parsed, 0, false);
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
unlk: function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
return Base.read_memory(e.opd[1].register || 'a7', e.opd[0].register, e.bits, false);
|
||||
},
|
||||
invalid: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(asm) {
|
||||
const expr = /([a-zA-Z]+)(\.[bwl])?\s+([\w.:_-]+\([\w. *,[\]]+\)|-?\([\w. *,[\]]+\)\+?|[\w./:_-]+)(?:,\s+([\w.:_-]+\([\w. *,[\]]+\)|-?\([\w. *,[\]]+\)\+?|[\w./:_-]+))?/;
|
||||
var token = asm.match(expr);
|
||||
token.shift();
|
||||
/*
|
||||
* [
|
||||
"move.l obj.__unctrl(d0.l * 4), -(a7)",
|
||||
"move",
|
||||
".l",
|
||||
"obj.__unctrl(d0.l * 4)",
|
||||
"-(a7)"
|
||||
* ]
|
||||
* token[0] = assembly
|
||||
* token[1] = mnem
|
||||
* token[2] = bits
|
||||
* token[3] = opd0
|
||||
* token[4] = opd1
|
||||
*/
|
||||
|
||||
var mnem = token.shift();
|
||||
var bits = token.shift();
|
||||
|
||||
var ret = {
|
||||
mnem: mnem,
|
||||
bits: _to_size(bits ? bits.substring(1) : 'l'),
|
||||
opd: token.filter(function(x) {
|
||||
return x ? true : false;
|
||||
}).map(function(x) {
|
||||
var d = {
|
||||
presub: false,
|
||||
postadd: false,
|
||||
pointer: null,
|
||||
register: null,
|
||||
};
|
||||
|
||||
var isreg = x.indexOf('(') < 0;
|
||||
var ptr = x.replace(/([\w.:_-][\w.:_-]+)\(/, '$1 + ').replace(/(-?\()|\)\+?/g, ' ').replace(/,\sinvalid\.[bwl]/, '');
|
||||
var p = x.replace(/[(),]/g, ' ').replace(/\s+/g, ' ');
|
||||
p = p.replace(/invalid\.[bwl]/, '');
|
||||
p = p.trim().split(' ');
|
||||
if (p[0] == '-') {
|
||||
d.presub = true;
|
||||
p.shift();
|
||||
} else if (p[p.length - 1] == '+') {
|
||||
d.postadd = true;
|
||||
p.pop();
|
||||
}
|
||||
if (isreg) {
|
||||
d.register = p.pop();
|
||||
} else if (ptr.indexOf('-') > 0 || ptr.indexOf('+') > 0 || ptr.indexOf('*') > 0) {
|
||||
d.pointer = ptr.replace(/(\d)\.[bwl]/, '$1').replace(/,/g, ' +').trim();
|
||||
d.register = ptr.match(/[ad]\d+/)[0];
|
||||
} else {
|
||||
d.pointer = p.join(' + ');
|
||||
d.register = p[0];
|
||||
}
|
||||
return d;
|
||||
}),
|
||||
};
|
||||
return ret;
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: '?',
|
||||
b: '?'
|
||||
},
|
||||
leave: false,
|
||||
vars: []
|
||||
};
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
@@ -1,26 +1,10 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
|
||||
var Instruction = require('libdec/core/instruction');
|
||||
var Base = require('libdec/core/base');
|
||||
var Variable = require('libdec/core/variable');
|
||||
var Long = require('libdec/long');
|
||||
import Instruction from '../core/instruction.js';
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
import Long from '../long.js';
|
||||
|
||||
const _mips_registers = [
|
||||
'zero', 'at', 'v0', 'v1',
|
||||
@@ -152,10 +136,10 @@
|
||||
];
|
||||
var address = [
|
||||
function(e, addr) {
|
||||
return Long.fromString(_hex(e.opd[1]) + '0000', true, 16);
|
||||
return Long.from(_hex(e.opd[1]) + '0000', true, 16);
|
||||
},
|
||||
function(e, addr) {
|
||||
var n = Long.fromString(_hex(e.opd[2]), e.mnem.indexOf('u') > 0, 16);
|
||||
var n = Long.from(_hex(e.opd[2]), e.mnem.indexOf('u') > 0, 16);
|
||||
var op = e.mnem.replace(/[iu]/g, '');
|
||||
return addr[op](n);
|
||||
},
|
||||
@@ -178,8 +162,8 @@
|
||||
}
|
||||
--i;
|
||||
if (instr.parsed.opd[0] != 'gp') {
|
||||
instr.string = Global.xrefs.find_string(addr);
|
||||
instr.symbol = Global.xrefs.find_symbol(addr);
|
||||
instr.string = Global().xrefs.find_string(addr);
|
||||
instr.symbol = Global().xrefs.find_symbol(addr);
|
||||
addr = instr.string ? Variable.string(instr.string) : (instr.symbol || ('0x' + addr.toString(16)).replace(/0x-/, '-0x'));
|
||||
instr.valid = true;
|
||||
} else {
|
||||
@@ -233,7 +217,7 @@
|
||||
'bgtz', 'bgez', 'beq', 'bne', 'bnel'
|
||||
];
|
||||
|
||||
return {
|
||||
export default {
|
||||
instructions: {
|
||||
'nop': function(instr) {
|
||||
return Base.nop();
|
||||
@@ -537,4 +521,3 @@
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
});
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,585 @@
|
||||
// SPDX-FileCopyrightText: 2021-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
import Long from '../long.js';
|
||||
|
||||
function load_bits(instr, bits, unsigned) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var arg = e.opd[1].replace(/\)/, '').split('(');
|
||||
if (arg[0].indexOf('-sym.') == 0 && arg[1] == 'gp') {
|
||||
return Base.assign(e.opd[0], arg[0].replace(/^-/, ''));
|
||||
} else if (arg[1] == '0') {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(arg[0], e.opd[0], bits, !unsigned);
|
||||
} else if (arg[0] == '0') {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(arg[1], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
arg[0] = arg[0].length > 0 ? parseInt(arg[0]) : NaN;
|
||||
if (!isNaN(arg[0])) {
|
||||
if (arg[0] < 0) {
|
||||
arg[0] = " - 0x" + Math.abs(arg[0]).toString(16);
|
||||
} else {
|
||||
arg[0] = " + 0x" + arg[0].toString(16);
|
||||
}
|
||||
return Base.read_memory(arg[1] + arg[0], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
return Base.read_memory(arg[1], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
|
||||
function store_bits(instr, bits, unsigned) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var arg = e.opd[1].replace(/\)/, '').split('(');
|
||||
if (arg[1] == '0') {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(arg[0], e.opd[0], bits, !unsigned);
|
||||
} else if (arg[0] == '0') {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(arg[1], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
arg[0] = arg[0].length > 0 ? parseInt(arg[0]) : NaN;
|
||||
if (!isNaN(arg[0])) {
|
||||
if (arg[0] < 0) {
|
||||
arg[0] = " - 0x" + Math.abs(arg[0]).toString(16);
|
||||
} else {
|
||||
arg[0] = " + 0x" + arg[0].toString(16);
|
||||
}
|
||||
return Base.write_memory(arg[1] + arg[0], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
return Base.write_memory(arg[1], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
|
||||
function _csr_assign_3arg(instr) {
|
||||
instr.setBadJump();
|
||||
var dst = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
return Base.assign(dst, src);
|
||||
}
|
||||
|
||||
function _generic_assign(instr) {
|
||||
instr.setBadJump();
|
||||
var dst = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
return Base.assign(dst, src);
|
||||
}
|
||||
|
||||
function _generic_add(instr) {
|
||||
instr.setBadJump();
|
||||
var dst = instr.parsed.opd[0];
|
||||
var src0 = instr.parsed.opd[1];
|
||||
var src1 = instr.parsed.opd[2];
|
||||
return Base.add(dst, src0, src1);
|
||||
}
|
||||
|
||||
function op_bits3(instr, op) {
|
||||
instr.setBadJump();
|
||||
var dst = instr.parsed.opd[0];
|
||||
var src0 = instr.parsed.opd[1];
|
||||
var src1 = instr.parsed.opd[2];
|
||||
return op(dst, src0, src1);
|
||||
}
|
||||
|
||||
function assign_call(instr, name) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var args = instr.parsed.opd.slice(1);
|
||||
return Base.special(dst + ' = ' + name + '( ', args.join(', ') + ')');
|
||||
}
|
||||
|
||||
function _compare(instr, context, instructions, cmp, zero) {
|
||||
instr.conditional(instr.parsed.opd[0], zero ? "0" : instr.parsed.opd[1], cmp);
|
||||
var arg = instr.parsed.opd[instr.parsed.opd.length - 1];
|
||||
if (arg.indexOf('0x') == 0) {
|
||||
instr.jump = Long.from(arg, true);
|
||||
}
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
function _is_last_instruction(instr, instructions) {
|
||||
return instructions.indexOf(instr) == (instructions.length - 1);
|
||||
}
|
||||
|
||||
function _hex(value) {
|
||||
return parseInt(value).toString(16);
|
||||
}
|
||||
|
||||
function lui32(instr, start, instructions, context) {
|
||||
var addr = null;
|
||||
var check = [
|
||||
function(e, r) {
|
||||
return e.mnem == 'lui' && e.opd[0] == r;
|
||||
},
|
||||
function(e, r) {
|
||||
if (e.mnem == 'nop') {
|
||||
return true;
|
||||
}
|
||||
return (e.mnem == 'ori' && (e.opd[0] == r || e.opd[0].indexOf('a') == 0) && e.opd[1] == r) ||
|
||||
(e.mnem == 'addi' && (e.opd[0] == r || e.opd[0].indexOf('a') == 0) && e.opd[1] == r) ||
|
||||
(e.mnem == 'addiu' && (e.opd[0] == r || e.opd[0].indexOf('a') == 0) && e.opd[1] == r);
|
||||
},
|
||||
];
|
||||
var address = [
|
||||
function(e, addr) {
|
||||
var imm32 = instr.parsed.opd[1] << 12;
|
||||
return Long.from(imm32, true);
|
||||
},
|
||||
function(e, addr) {
|
||||
var n = Long.from(_hex(e.opd[2]), e.mnem.indexOf('u') > 0, 16);
|
||||
var op = e.mnem.replace(/[iu]/g, '');
|
||||
return addr[op](n);
|
||||
},
|
||||
];
|
||||
var step = 0;
|
||||
var i;
|
||||
for (i = start; i < instructions.length && step < check.length; ++i) {
|
||||
var elem = instructions[i].parsed;
|
||||
if (!check[step](elem, instr.parsed.opd[0])) {
|
||||
break;
|
||||
}
|
||||
addr = address[step](elem, addr);
|
||||
step++;
|
||||
if (instr.parsed.opd[0] == instructions[i].parsed.opd[0]) {
|
||||
instructions[i].valid = false;
|
||||
} else {
|
||||
instr.valid = true;
|
||||
instr = instructions[i];
|
||||
}
|
||||
}
|
||||
--i;
|
||||
if (instr.parsed.opd[0] != 'gp') {
|
||||
instr.string = Global().xrefs.find_string(addr);
|
||||
instr.symbol = Global().xrefs.find_symbol(addr);
|
||||
addr = instr.string ? Variable.string(instr.string) : (instr.symbol || ('0x' + addr.toString(16)).replace(/0x-/, '-0x'));
|
||||
instr.valid = true;
|
||||
} else {
|
||||
addr = ('0x' + addr.toString(16)).replace(/0x-/, '-0x');
|
||||
instr.valid = false;
|
||||
}
|
||||
instr.code = Base.assign(instr.parsed.opd[0], addr);
|
||||
return i;
|
||||
}
|
||||
|
||||
export default {
|
||||
instructions: {
|
||||
auipc: function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var imm32 = parseInt(instr.parsed.opd[1]);
|
||||
var src = instr.location.add(imm32);
|
||||
return Base.assign(dst, '0x' + src.toString(16));
|
||||
},
|
||||
lui: function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var imm20 = instr.parsed.opd[1] << 12;
|
||||
return Base.assign(dst, '0x' + imm20.toString(16));
|
||||
},
|
||||
lb: function(instr) {
|
||||
return load_bits(instr, 8, false);
|
||||
},
|
||||
lh: function(instr) {
|
||||
return load_bits(instr, 16, false);
|
||||
},
|
||||
lw: function(instr) {
|
||||
return load_bits(instr, 32, false);
|
||||
},
|
||||
lwu: function(instr) {
|
||||
return load_bits(instr, 32, true);
|
||||
},
|
||||
ld: function(instr) {
|
||||
return load_bits(instr, 64, false);
|
||||
},
|
||||
lbu: function(instr) {
|
||||
return load_bits(instr, 8, true);
|
||||
},
|
||||
lhu: function(instr) {
|
||||
return load_bits(instr, 16, true);
|
||||
},
|
||||
'lr.w': function(instr) {
|
||||
return load_bits(instr, 32, false);
|
||||
},
|
||||
'lr.d': function(instr) {
|
||||
return load_bits(instr, 64, false);
|
||||
},
|
||||
sb: function(instr) {
|
||||
return store_bits(instr, 8, false);
|
||||
},
|
||||
sh: function(instr) {
|
||||
return store_bits(instr, 16, false);
|
||||
},
|
||||
sw: function(instr) {
|
||||
return store_bits(instr, 32, false);
|
||||
},
|
||||
sd: function(instr) {
|
||||
return store_bits(instr, 64, false);
|
||||
},
|
||||
'sc.w': function(instr) {
|
||||
return store_bits(instr, 32, false);
|
||||
},
|
||||
'sc.d': function(instr) {
|
||||
return store_bits(instr, 64, false);
|
||||
},
|
||||
'sext.w': function(instr) {
|
||||
return Base.extend(instr.parsed.opd[0], instr.parsed.opd[1], 32);
|
||||
},
|
||||
add: _generic_add,
|
||||
addi: _generic_add,
|
||||
addw: _generic_add,
|
||||
addiw: _generic_add,
|
||||
and: function(instr) {
|
||||
return op_bits3(instr, Base.and);
|
||||
},
|
||||
andi: function(instr) {
|
||||
return op_bits3(instr, Base.and);
|
||||
},
|
||||
sub: function(instr) {
|
||||
return op_bits3(instr, Base.subtract);
|
||||
},
|
||||
subw: function(instr) {
|
||||
return op_bits3(instr, Base.subtract);
|
||||
},
|
||||
mul: function(instr) {
|
||||
return op_bits3(instr, Base.multiply);
|
||||
},
|
||||
mulh: function(instr) {
|
||||
return op_bits3(instr, Base.multiply);
|
||||
},
|
||||
mulw: function(instr) {
|
||||
return op_bits3(instr, Base.multiply);
|
||||
},
|
||||
srli: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
srai: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
sll: function(instr) {
|
||||
return op_bits3(instr, Base.shift_left);
|
||||
},
|
||||
slli: function(instr) {
|
||||
return op_bits3(instr, Base.shift_left);
|
||||
},
|
||||
srlw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
srliw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
sraw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
sraiw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
sllw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_left);
|
||||
},
|
||||
slliw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_left);
|
||||
},
|
||||
div: function(instr) {
|
||||
return op_bits3(instr, Base.divide);
|
||||
},
|
||||
divu: function(instr) {
|
||||
return op_bits3(instr, Base.divide);
|
||||
},
|
||||
rem: function(instr) {
|
||||
return op_bits3(instr, Base.module);
|
||||
},
|
||||
remu: function(instr) {
|
||||
return op_bits3(instr, Base.module);
|
||||
},
|
||||
divw: function(instr) {
|
||||
return op_bits3(instr, Base.divide);
|
||||
},
|
||||
divuw: function(instr) {
|
||||
return op_bits3(instr, Base.divide);
|
||||
},
|
||||
remw: function(instr) {
|
||||
return op_bits3(instr, Base.module);
|
||||
},
|
||||
remuw: function(instr) {
|
||||
return op_bits3(instr, Base.module);
|
||||
},
|
||||
or: function(instr) {
|
||||
return op_bits3(instr, Base.or);
|
||||
},
|
||||
ori: function(instr) {
|
||||
return op_bits3(instr, Base.or);
|
||||
},
|
||||
xor: function(instr) {
|
||||
return op_bits3(instr, Base.xor);
|
||||
},
|
||||
xori: function(instr) {
|
||||
return op_bits3(instr, Base.xor);
|
||||
},
|
||||
'slt': function(instr) {
|
||||
var e = instr.parsed;
|
||||
return Base.conditional_assign(e.opd[0], e.opd[1], e.opd[2], 'LT', '1', '0');
|
||||
},
|
||||
'slti': function(instr) {
|
||||
var e = instr.parsed;
|
||||
return Base.conditional_assign(e.opd[0], e.opd[1], e.opd[2], 'LT', '1', '0');
|
||||
},
|
||||
'sltiu': function(instr) {
|
||||
var e = instr.parsed;
|
||||
var arg0 = Variable.local(e.opd[1], 32);
|
||||
var arg1 = Variable.local(e.opd[2], 32);
|
||||
return Base.conditional_assign(e.opd[0], arg0, arg1, 'LT', '1', '0');
|
||||
},
|
||||
'sltu': function(instr) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[2] == 'zero') {
|
||||
e.opd[2] = '0';
|
||||
}
|
||||
var arg0 = Variable.local(e.opd[1], 32);
|
||||
var arg1 = Variable.local(e.opd[2], 32);
|
||||
return Base.conditional_assign(e.opd[0], arg0, arg1, 'LT', '1', '0');
|
||||
},
|
||||
'amoadd.w': _generic_add,
|
||||
'amoor.w': function(instr) {
|
||||
return op_bits3(instr, Base.or);
|
||||
},
|
||||
'amoxor.w': function(instr) {
|
||||
return op_bits3(instr, Base.xor);
|
||||
},
|
||||
'amoand.w': function(instr) {
|
||||
return op_bits3(instr, Base.and);
|
||||
},
|
||||
'amomin.w': function(instr) {
|
||||
return assign_call(instr, 'min');
|
||||
},
|
||||
'amominu.w': function(instr) {
|
||||
return assign_call(instr, 'min');
|
||||
},
|
||||
'amomax.w': function(instr) {
|
||||
return assign_call(instr, 'max');
|
||||
},
|
||||
'amomaxu.w': function(instr) {
|
||||
return assign_call(instr, 'max');
|
||||
},
|
||||
'amoadd.d': _generic_add,
|
||||
'amoor.d': function(instr) {
|
||||
return op_bits3(instr, Base.or);
|
||||
},
|
||||
'amoxor.d': function(instr) {
|
||||
return op_bits3(instr, Base.xor);
|
||||
},
|
||||
'amoand.d': function(instr) {
|
||||
return op_bits3(instr, Base.and);
|
||||
},
|
||||
'amomin.d': function(instr) {
|
||||
return assign_call(instr, 'min');
|
||||
},
|
||||
'amominu.d': function(instr) {
|
||||
return assign_call(instr, 'min');
|
||||
},
|
||||
'amomax.d': function(instr) {
|
||||
return assign_call(instr, 'max');
|
||||
},
|
||||
'amomaxu.d': function(instr) {
|
||||
return assign_call(instr, 'max');
|
||||
},
|
||||
mv: _generic_assign,
|
||||
li: _generic_assign,
|
||||
csrr: _generic_assign,
|
||||
csrw: _generic_assign,
|
||||
csrc: _generic_assign,
|
||||
csrs: _generic_assign,
|
||||
csrri: _generic_assign,
|
||||
csrwi: _generic_assign,
|
||||
csrci: _generic_assign,
|
||||
csrsi: _generic_assign,
|
||||
csrrwi: _csr_assign_3arg,
|
||||
csrrci: _csr_assign_3arg,
|
||||
csrrsi: _csr_assign_3arg,
|
||||
beqz: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'EQ', true);
|
||||
},
|
||||
bnez: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'NE', true);
|
||||
},
|
||||
bltz: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LT', true);
|
||||
},
|
||||
blez: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LE', true);
|
||||
},
|
||||
bgtz: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GT', true);
|
||||
},
|
||||
bgez: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GE', true);
|
||||
},
|
||||
beq: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'EQ', false);
|
||||
},
|
||||
bne: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'NE', false);
|
||||
},
|
||||
blt: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LT', false);
|
||||
},
|
||||
ble: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LE', false);
|
||||
},
|
||||
bgt: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GT', false);
|
||||
},
|
||||
bge: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GE', false);
|
||||
},
|
||||
bltu: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LT', false);
|
||||
},
|
||||
bleu: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LE', false);
|
||||
},
|
||||
bgtu: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GT', false);
|
||||
},
|
||||
bgeu: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GE', false);
|
||||
},
|
||||
jal: function(instr, context, instructions) {
|
||||
var fcn_name = instr.parsed.opd[1].replace(/\./g, '_');
|
||||
if (fcn_name.indexOf('0x') == 0) {
|
||||
fcn_name = fcn_name.replace(/0x/, 'fcn_');
|
||||
}
|
||||
var args = [];
|
||||
var regs = ['a7', 'a6', 'a5', 'a4', 'a3', 'a2', 'a1', 'a0'];
|
||||
var found = 0,
|
||||
i;
|
||||
for (i = instructions.indexOf(instr) - 1; i >= 0; i--) {
|
||||
if (instructions[i].parsed.mnem.startsWith('b') ||
|
||||
instructions[i].parsed.mnem.startsWith('j')) {
|
||||
break;
|
||||
}
|
||||
var reg = instructions[i].parsed.opd[0];
|
||||
if (regs.indexOf(reg) >= 0) {
|
||||
found = parseInt(reg.substr(1, 3)) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < found; i++) {
|
||||
args.push('a' + i);
|
||||
}
|
||||
return Base.call(fcn_name, args);
|
||||
},
|
||||
jalr: function(instr) {
|
||||
var is_return = false;
|
||||
var arg = instr.parsed.opd[0];
|
||||
if (arg.indexOf('(') > 0) {
|
||||
arg = arg.replace(/\(|\)/g, ' ').trim().split(' ');
|
||||
if (arg[1] == 'ra') {
|
||||
is_return = true;
|
||||
}
|
||||
arg = arg[1] + ' + ' + arg[0];
|
||||
arg = arg.replace(/\+ -/, '-');
|
||||
}
|
||||
if (is_return || arg == 'ra') {
|
||||
if (is_return) {
|
||||
instr.comments.push('return address: ' + arg);
|
||||
}
|
||||
return Base.return(instr.parsed.opd[1]);
|
||||
}
|
||||
return Base.call(arg, [], true);
|
||||
},
|
||||
j: function(instr, context, instructions) {
|
||||
var arg = instr.parsed.opd[0];
|
||||
if (arg.indexOf('0x') == 0) {
|
||||
instr.jump = Long.from(arg);
|
||||
} else if (_is_last_instruction(instr, instructions)) {
|
||||
return Base.call(arg, []);
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
ret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
uret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
sret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
mret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
fence: function() {
|
||||
return Base.macro('memory_fence()', '#define memory_fence() __asm(fence)');
|
||||
},
|
||||
'fence.i': function() {
|
||||
return Base.macro('memory_fence_i()', '#define memory_fence_i() __asm(fence.i)');
|
||||
},
|
||||
'sfence.vma': function() {
|
||||
return Base.macro('memory_sfence_vma()', '#define memory_sfence_vma() __asm(sfence.vma)');
|
||||
},
|
||||
ebreak: function() {
|
||||
return Base.macro('environment_break()', '#define environment_break() __asm(ebreak)');
|
||||
},
|
||||
ecall: function() {
|
||||
return Base.macro('environment_call()', '#define environment_call() __asm(ecall)');
|
||||
},
|
||||
wfi: function() {
|
||||
return Base.macro('wait_for_interrupt()', '#define wait_for_interrupt() __asm(wfi)');
|
||||
},
|
||||
nop: function(instr) {
|
||||
return Base.nop();
|
||||
},
|
||||
invalid: function() {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(asm) {
|
||||
asm = asm.replace(/,/g, ' ').replace(/\s+/g, ' ').trim().split(' ').map(function(x) {
|
||||
if (x == 'zero') {
|
||||
return '0';
|
||||
}
|
||||
return x;
|
||||
});
|
||||
|
||||
var mnem = asm.shift();
|
||||
if (mnem.indexOf('amo') == 0) {
|
||||
// atomic ops.
|
||||
asm = asm.map(function(x) {
|
||||
return x.replace(/\(|\)/g, '');
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
mnem: mnem,
|
||||
opd: asm
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: null,
|
||||
b: null
|
||||
}
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions) {},
|
||||
postanalisys: function(instructions, context) {
|
||||
/* simplifies any load address 32/64 bit */
|
||||
for (var i = 0; i < instructions.length; i++) {
|
||||
if (instructions[i].parsed.mnem == 'lui') {
|
||||
i = lui32(instructions[i], i, instructions);
|
||||
}
|
||||
}
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,555 @@
|
||||
// SPDX-FileCopyrightText: 2019-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// https://www.rockbox.org/wiki/pub/Main/DataSheets/sh1_2p.pdf
|
||||
// http://www.shared-ptr.com/sh_insns.html
|
||||
|
||||
|
||||
import Instruction from '../core/instruction.js';
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
|
||||
const _s = 's';
|
||||
const _carry = 'c';
|
||||
const _mac = 'mac';
|
||||
|
||||
const _size_map = {
|
||||
"b": 8,
|
||||
"w": 16,
|
||||
"l": 32
|
||||
};
|
||||
|
||||
const _compare_map = {
|
||||
'eq': 'EQ',
|
||||
'hi': 'GT',
|
||||
'hs': 'GE',
|
||||
'ge': 'GE',
|
||||
'gt': 'GT',
|
||||
'pl': 'GT',
|
||||
'pz': 'GE',
|
||||
'str': 'str',
|
||||
};
|
||||
|
||||
const _reverse_map = {
|
||||
'EQ': 'NE',
|
||||
'GT': 'LE',
|
||||
'GE': 'LT',
|
||||
};
|
||||
|
||||
const _branch_list = [
|
||||
'bra',
|
||||
'braf',
|
||||
'bsr',
|
||||
'bsrf',
|
||||
'jmp',
|
||||
'jsr',
|
||||
'rte',
|
||||
'rts'
|
||||
];
|
||||
|
||||
function _set_conditional(instr, context, cmp, invert) {
|
||||
if (invert) {
|
||||
cmp = _reverse_map[cmp];
|
||||
}
|
||||
instr.conditional(context.cond.a, context.cond.b, cmp);
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
function _require_composed(instr, idx) {
|
||||
var e = instr.parsed;
|
||||
return e.opd[idx].increment || e.opd[idx].decrement;
|
||||
}
|
||||
|
||||
function _handle_pointer(instr, idx, ops) {
|
||||
var e = instr.parsed;
|
||||
var size = e.size || 32;
|
||||
if (e.opd[idx].increment) {
|
||||
ops.push(Base.increase(e.opd[idx].token, size / 8));
|
||||
} else if (e.opd[idx].decrement) {
|
||||
ops.unshift(Base.decrease(e.opd[idx].token, size / 8));
|
||||
}
|
||||
}
|
||||
|
||||
function _arg(instr, idx) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[idx].pointer) {
|
||||
// a great way to fix PC + N
|
||||
if (e.opd[idx].token.startsWith('pc +')) {
|
||||
var n = parseInt(e.opd[idx].token.substr('pc +'.length));
|
||||
var p = instr.location.add(n);
|
||||
e.opd[idx].token = '0x' + p.toString(16);
|
||||
}
|
||||
return Variable.pointer(e.opd[idx].toString(), e.size || 32, true);
|
||||
}
|
||||
return e.opd[idx].toString();
|
||||
}
|
||||
|
||||
function _op_move(instr) {
|
||||
var ops = [];
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
if (instr.parsed.size) {
|
||||
ops.push(Base.cast(dst, src, 'int' + instr.parsed.size + '_t'));
|
||||
} else {
|
||||
ops.push(Base.assign(dst, src));
|
||||
}
|
||||
if (_require_composed(instr, 0) || _require_composed(instr, 1)) {
|
||||
if (_require_composed(instr, 0)) {
|
||||
_handle_pointer(instr, 0, ops);
|
||||
} else {
|
||||
_handle_pointer(instr, 1, ops);
|
||||
}
|
||||
return Base.composed(ops);
|
||||
}
|
||||
return ops[0];
|
||||
}
|
||||
|
||||
var _is_last_instruction = function(instr, instructions) {
|
||||
return instructions.indexOf(instr) == (instructions.length - 1);
|
||||
};
|
||||
|
||||
export default {
|
||||
instructions: {
|
||||
mov: _op_move,
|
||||
mova: _op_move,
|
||||
movt: function(instr, context, instructions) {
|
||||
instr.parsed.opd[0].token = _carry;
|
||||
return _op_move(instr);
|
||||
},
|
||||
swap: function(instr, context, instructions) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.swap(dst, src, 32);
|
||||
},
|
||||
/*
|
||||
xtrct: function(instr, context, instructions) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.?????(dst, src);
|
||||
},
|
||||
*/
|
||||
add: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.add(dst, dst, src);
|
||||
},
|
||||
addc: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.add(dst, dst, src + ' + ' + _carry);
|
||||
},
|
||||
addv: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
instr.comments.push('if overflows, then ' + _carry + ' = 1.');
|
||||
return Base.add(dst, dst, src + ' + ' + _carry);
|
||||
},
|
||||
cmp: function(instr, context) {
|
||||
var a = _arg(instr, 0);
|
||||
var b = _arg(instr, 1);
|
||||
if (instr.parsed.cmp != 'str') {
|
||||
context.cond.a = a;
|
||||
context.cond.b = b;
|
||||
context.cond.cmp = instr.parsed.cmp;
|
||||
} else {
|
||||
context.cond.a = (a === b) ? a : '(' + a + ' & ' + b + ')';
|
||||
context.cond.b = '0';
|
||||
context.cond.cmp = 'EQ';
|
||||
}
|
||||
},
|
||||
tst: function(instr, context) {
|
||||
var a = _arg(instr, 0);
|
||||
var b = _arg(instr, 1);
|
||||
context.cond.a = (a === b) ? a : '(' + a + ' & ' + b + ')';
|
||||
context.cond.b = '0';
|
||||
context.cond.cmp = 'EQ';
|
||||
},
|
||||
div1: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.divide(dst, dst, src);
|
||||
},
|
||||
dmuls: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(dst, dst, src);
|
||||
},
|
||||
dt: function(instr) {
|
||||
var dst = _arg(instr, 1);
|
||||
return Base.decrease(dst, dst, 1);
|
||||
},
|
||||
exts: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.cast(dst, src, 'int' + instr.parsed.size + '_t');
|
||||
},
|
||||
extu: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.cast(dst, src, 'uint' + instr.parsed.size + '_t');
|
||||
},
|
||||
mul: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(_mac, dst, src);
|
||||
},
|
||||
muls: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(_mac, dst, src);
|
||||
},
|
||||
mulu: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(_mac, dst, src);
|
||||
},
|
||||
neg: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.negate(dst, src);
|
||||
},
|
||||
negc: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.composed([
|
||||
Base.negate(dst, src),
|
||||
Base.subtract(dst, dst, _carry),
|
||||
]);
|
||||
},
|
||||
sub: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.subtract(dst, dst, src);
|
||||
},
|
||||
subc: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.composed([
|
||||
Base.subtract(dst, dst, src),
|
||||
Base.subtract(dst, dst, _carry),
|
||||
]);
|
||||
},
|
||||
subv: function(instr) {
|
||||
instr.comments.push('if underflows, then ' + _carry + ' = 1.');
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.subtract(dst, dst, src);
|
||||
},
|
||||
and: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.and(dst, dst, src);
|
||||
},
|
||||
not: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.not(dst, src);
|
||||
},
|
||||
or: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.or(dst, dst, src);
|
||||
},
|
||||
xor: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.xor(dst, dst, src);
|
||||
},
|
||||
rotl: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.rotate_left(src, src, 1, 32);
|
||||
},
|
||||
rotr: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.rotate_right(src, src, 1, 32);
|
||||
},
|
||||
rotcl: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.rotate_left(src, src, 1, 32);
|
||||
},
|
||||
rotcr: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.rotate_right(src, src, 1, 32);
|
||||
},
|
||||
shal: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 1);
|
||||
},
|
||||
shar: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 1);
|
||||
},
|
||||
shll: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 1);
|
||||
},
|
||||
shlr: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 1);
|
||||
},
|
||||
shll2: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 2);
|
||||
},
|
||||
shlr2: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 2);
|
||||
},
|
||||
shll8: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 8);
|
||||
},
|
||||
shlr8: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 8);
|
||||
},
|
||||
shll16: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 16);
|
||||
},
|
||||
shlr16: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 16);
|
||||
},
|
||||
ldc: _op_move,
|
||||
lds: _op_move,
|
||||
stc: _op_move,
|
||||
sts: _op_move,
|
||||
clrt: function(instr) {
|
||||
return Base.assign(_carry, '0');
|
||||
},
|
||||
clrs: function(instr) {
|
||||
return Base.assign(_s, '0');
|
||||
},
|
||||
sett: function(instr) {
|
||||
return Base.assign(_carry, '1');
|
||||
},
|
||||
sets: function(instr) {
|
||||
return Base.assign(_s, '1');
|
||||
},
|
||||
fmov: _op_move,
|
||||
flds: _op_move,
|
||||
fsts: _op_move,
|
||||
fldi0: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.assign(src, '0.0');
|
||||
},
|
||||
fldi1: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.assign(src, '1.0');
|
||||
},
|
||||
fabs: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.and(src, src, '0x7FFFFFFF');
|
||||
},
|
||||
fneg: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.negate(dst, src);
|
||||
},
|
||||
fadd: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.add(dst, dst, src);
|
||||
},
|
||||
fsub: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.subtract(dst, dst, src);
|
||||
},
|
||||
fmul: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(dst, dst, src);
|
||||
},
|
||||
fdiv: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.divide(dst, dst, src);
|
||||
},
|
||||
fsqrt: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.assign(dst, Base.call('sqrt', [src]));
|
||||
},
|
||||
fcmp: function(instr, context) {
|
||||
var a = _arg(instr, 0);
|
||||
var b = _arg(instr, 1);
|
||||
context.cond.a = a;
|
||||
context.cond.b = b;
|
||||
context.cond.cmp = instr.parsed.cmp;
|
||||
},
|
||||
float: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.cast(dst, src, 'float');
|
||||
},
|
||||
ftrc: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.cast(dst, src, 'int32_t');
|
||||
},
|
||||
fsrra: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.special(dst + ' = 1.0 / sqrt (' + src + ')');
|
||||
},
|
||||
fsca: function(instr) {
|
||||
var dst1 = _arg(instr, 0);
|
||||
var dst2 = 'fr' + (parseInt(dst1.substr(2)) + 1).toString(16);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.composed([
|
||||
Base.assign(dst1, Base.call('sin', [src])),
|
||||
Base.assign(dst2, Base.call('cos', [src]))
|
||||
]);
|
||||
},
|
||||
bt: function(instr, context, instructions) {
|
||||
return _set_conditional(instr, context, 'EQ');
|
||||
},
|
||||
bf: function(instr, context, instructions) {
|
||||
return _set_conditional(instr, context, 'NE');
|
||||
},
|
||||
jsr: function(instr, context, instructions) {
|
||||
var e = instr.parsed.opd;
|
||||
return Base.call(Variable.functionPointer(e[1].token), []);
|
||||
},
|
||||
bsr: function(instr, context, instructions) {
|
||||
var e = instr.parsed.opd;
|
||||
if (_is_last_instruction(instr, instructions)) {
|
||||
return Base.call(Variable.functionPointer(e[1].token), []);
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
bra: function(instr) {
|
||||
var callname = _arg(instr, 1);
|
||||
if (callname.startsWith('0x')) {
|
||||
callname = Variable.functionPointer(callname);
|
||||
}
|
||||
return Base.call(callname, []);
|
||||
},
|
||||
braf: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.call(Variable.functionPointer(src), []);
|
||||
},
|
||||
rte: function() {
|
||||
return Base.return();
|
||||
},
|
||||
rts: function() {
|
||||
return Base.return();
|
||||
},
|
||||
nop: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
invalid: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(assembly) {
|
||||
assembly = assembly.trim().toLowerCase();
|
||||
assembly = assembly.replace(/,/g, ' ');
|
||||
assembly = assembly.replace(/\s+/g, ' ');
|
||||
|
||||
/*
|
||||
Regex explaination:
|
||||
^
|
||||
(\w+)
|
||||
\.?
|
||||
([wlb])? // word/long/byte
|
||||
([sn])? // delayed branch
|
||||
\/?
|
||||
(hi|eq|hs|pl|pz|str)? // compare flags
|
||||
? // space might not exists (aka no 1st and 2nd param)
|
||||
(
|
||||
(
|
||||
(@)? // Indirect or Direct access
|
||||
(
|
||||
(\(\w+\s\w+\)) A + B value
|
||||
|
|
||||
(-)? Pre-decrement
|
||||
([\w._]+) register
|
||||
(\+)? Post-increment
|
||||
)
|
||||
)
|
||||
)?
|
||||
? // space might not exists (aka no 2nd param)
|
||||
(
|
||||
(
|
||||
(@)? // Indirect or Direct access
|
||||
(
|
||||
(\(\w+\s\w+\)) A + B value
|
||||
|
|
||||
(-)? Pre-decrement
|
||||
([\w._]+) register
|
||||
(\+)? Post-increment
|
||||
)
|
||||
)
|
||||
)?
|
||||
$
|
||||
*/
|
||||
var tokens = assembly.match(/^(\w+)\.?([wlb])?([sn])?\/?(hi|eq|hs|pl|pz|str)? ?(((@)?((\(\w+\s\w+\))|(-)?([\w._]+)(\+)?)))? ?(((@)?((\(\w+\s\w+\))|(-)?([\w._]+)(\+)?)))?$/);
|
||||
var mnem = tokens[1];
|
||||
var size = _size_map[tokens[2]];
|
||||
var delayed = tokens[3] == 's';
|
||||
var cmp = _compare_map[tokens[4]];
|
||||
var opd = [{
|
||||
pointer: tokens[7] == '@',
|
||||
decrement: tokens[10] == '-',
|
||||
increment: tokens[12] == '+',
|
||||
token: tokens[11] || (tokens[9] ? tokens[9].replace(/\((\w+)\s(\w+)\)/g, '$2 + $1') : null),
|
||||
toString: function() {
|
||||
return this.token || '(null)';
|
||||
}
|
||||
}, {
|
||||
pointer: tokens[15] == '@',
|
||||
decrement: tokens[18] == '-',
|
||||
increment: tokens[20] == '+',
|
||||
token: tokens[19] || (tokens[17] ? tokens[17].replace(/\((\w+)\s(\w+)\)/g, '$2 + $1') : null),
|
||||
toString: function() {
|
||||
return this.token || '(null)';
|
||||
}
|
||||
}];
|
||||
|
||||
return {
|
||||
mnem: mnem,
|
||||
delayed: delayed,
|
||||
cmp: cmp,
|
||||
size: size,
|
||||
opd: opd.reverse()
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
cmp: null,
|
||||
a: '?',
|
||||
b: '?'
|
||||
}
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions, context) {
|
||||
/* delayed branch fix */
|
||||
for (var i = 0; i < (instructions.length - 1); i++) {
|
||||
var op = instructions[i].parsed.mnem;
|
||||
if (_branch_list.indexOf(op) >= 0 && instructions[i + 1].parsed.mnem != 'nop') {
|
||||
Instruction.swap(instructions, i, i + 1);
|
||||
++i;
|
||||
} else if (instructions[i].parsed.delayed && instructions[i + 1].parsed.mnem != 'nop') {
|
||||
Instruction.swap(instructions, i, i + 1);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
},
|
||||
postanalisys: function(instructions, context) {},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,395 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Instruction from '../core/instruction.js';
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
import Long from '../long.js';
|
||||
|
||||
var _call_fix_name = function(name) {
|
||||
if (typeof name != 'string') {
|
||||
return name;
|
||||
}
|
||||
if (name.indexOf('fcn.') == 0 || name.indexOf('func.') == 0) {
|
||||
return name.replace(/[.:]/g, '_').replace(/__+/g, '_');
|
||||
}
|
||||
return name.replace(/\[reloc\.|\]/g, '').replace(/[.:]/g, '_').replace(/__+/g, '_').replace(/_[0-9a-f]+$/, '').replace(/^_+/, '');
|
||||
};
|
||||
|
||||
var _load = function(instr, context, instruction, signed, bits) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[0].indexOf('+') > 0) {
|
||||
var arg = e.opd[0].split('+');
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
if (arg[1].indexOf('-') < 0) {
|
||||
ops.push(Base.add(value, arg[0], arg[1]));
|
||||
} else {
|
||||
ops.push(Base.subtract(value, arg[0], arg[1].replace(/-/, '')));
|
||||
}
|
||||
ops.push(Base.read_memory(value, e.opd[1], bits, signed));
|
||||
return Base.composed(ops);
|
||||
}
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(e.opd[0], e.opd[1], bits, signed);
|
||||
};
|
||||
|
||||
var _store = function(instr, context, instruction, signed, bits) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[1].indexOf('+') > 0) {
|
||||
var arg = e.opd[1].split('+');
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
if (arg[1].indexOf('-') < 0) {
|
||||
ops.push(Base.add(value, arg[0], arg[1]));
|
||||
} else {
|
||||
ops.push(Base.subtract(value, arg[0], arg[1].replace(/-/, '')));
|
||||
}
|
||||
ops.push(Base.write_memory(value, e.opd[0], bits, signed));
|
||||
return Base.composed(ops);
|
||||
}
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(e.opd[1], e.opd[0], bits, signed);
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, type) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _compare = function(instr, context, instructions) {
|
||||
context.cond.a = instr.parsed.opd[0];
|
||||
context.cond.b = instr.parsed.opd[1];
|
||||
};
|
||||
|
||||
var sethi32 = function(instr, start, instructions) {
|
||||
var addr = null;
|
||||
var check = [
|
||||
function(e, r) {
|
||||
return e.mnem == 'sethi' && e.opd[1] == r;
|
||||
},
|
||||
function(e, r) {
|
||||
if (e.mnem == 'nop') {
|
||||
return true;
|
||||
}
|
||||
return (e.mnem == 'or' && e.opd[0] == r && e.opd[2] == r) || (e.mnem == 'add' && e.opd[0] == r && e.opd[2] == r);
|
||||
}
|
||||
];
|
||||
var address = [
|
||||
function(e, addr) {
|
||||
var v = Long.from(parseInt(e.opd[0]).toString(16), false, 16);
|
||||
return v.shl(10);
|
||||
},
|
||||
function(e, addr) {
|
||||
var n = Long.from(parseInt(e.opd[1]).toString(16), false, 16);
|
||||
return addr[e.mnem](n);
|
||||
},
|
||||
];
|
||||
var step = 0;
|
||||
var i;
|
||||
for (i = start; i < instructions.length && step < check.length; ++i) {
|
||||
var elem = instructions[i].parsed;
|
||||
if (!check[step](elem, instr.parsed.opd[1])) {
|
||||
break;
|
||||
}
|
||||
addr = address[step](elem, addr);
|
||||
step++;
|
||||
instructions[i].code = Base.nop();
|
||||
}
|
||||
if (addr) {
|
||||
--i;
|
||||
addr = '0x' + addr.toString(16);
|
||||
instr.code = Base.assign(instr.parsed.opd[1], addr.replace(/0x-/, '-0x'));
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
var _branch_list = [
|
||||
'ba', 'bn', 'be', 'bne', 'bl', 'ble', 'bge', 'bg',
|
||||
'blu', 'bleu', 'bgeu', 'bgu', 'bpos', 'bneg',
|
||||
'bcs', 'bcc', 'bvs', 'bvc', 'call', 'jmp', 'jmpl',
|
||||
'ret', 'retl', 'rett'
|
||||
];
|
||||
|
||||
export default {
|
||||
instructions: {
|
||||
add: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
addcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
addx: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
addxcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
and: function(instr, context, instructions) {
|
||||
return Base.and(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
andcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
return Base.and(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
andn: function(instr, context, instructions) {
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
ops.push(Base.not(value, instr.parsed.opd[1]));
|
||||
ops.push(Base.and(instr.parsed.opd[2], instr.parsed.opd[0], value));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
andncc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
ops.push(Base.not(value, instr.parsed.opd[1]));
|
||||
ops.push(Base.and(instr.parsed.opd[2], instr.parsed.opd[0], value));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
ba: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
bn: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
be: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'EQ');
|
||||
},
|
||||
bne: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'NE');
|
||||
},
|
||||
bg: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bge: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
bl: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
ble: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
blu: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
bleu: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
bgeu: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
bgu: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bpos: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
bneg: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
call: function(instr, context, instructions) {
|
||||
return Base.call(_call_fix_name(instr.parsed.opd[0]), []);
|
||||
},
|
||||
cmp: _compare,
|
||||
ldub: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, false, 8);
|
||||
},
|
||||
ldsb: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, true, 8);
|
||||
},
|
||||
lduh: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, false, 16);
|
||||
},
|
||||
ldsh: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, true, 16);
|
||||
},
|
||||
ld: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, false, 32);
|
||||
},
|
||||
ldd: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, false, 64);
|
||||
},
|
||||
mov: function(instr, context, instructions) {
|
||||
return Base.assign(instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
nop: function(instr) {
|
||||
return Base.nop();
|
||||
},
|
||||
or: function(instr, context, instructions) {
|
||||
return Base.or(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
orcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
return Base.or(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
orn: function(instr, context, instructions) {
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
ops.push(Base.not(value, instr.parsed.opd[1]));
|
||||
ops.push(Base.or(instr.parsed.opd[2], instr.parsed.opd[0], value));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
orncc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
ops.push(Base.not(value, instr.parsed.opd[1]));
|
||||
ops.push(Base.or(instr.parsed.opd[2], instr.parsed.opd[0], value));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
ret: function(instr, context, instructions) {
|
||||
return Base.return();
|
||||
},
|
||||
retl: function(instr, context, instructions) {
|
||||
return Base.return();
|
||||
},
|
||||
rett: function(instr, context, instructions) {
|
||||
return Base.return();
|
||||
},
|
||||
restore: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
save: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
sethi: function(instr, context, instructions) {
|
||||
var v = Long.from(parseInt(instr.parsed.opd[0]).toString(16), true, 16);
|
||||
return Base.assign(instr.parsed.opd[1], '0x' + v.shl(10).toString(16));
|
||||
},
|
||||
sll: function(instr, context, instructions) {
|
||||
return Base.shift_left(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
srl: function(instr, context, instructions) {
|
||||
return Base.shift_right(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
sra: function(instr, context, instructions) {
|
||||
return Base.shift_right(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
stb: function(instr, context, instructions) {
|
||||
return _store(instr, context, instructions, false, 8);
|
||||
},
|
||||
sth: function(instr, context, instructions) {
|
||||
return _store(instr, context, instructions, false, 16);
|
||||
},
|
||||
st: function(instr, context, instructions) {
|
||||
return _store(instr, context, instructions, false, 32);
|
||||
},
|
||||
std: function(instr, context, instructions) {
|
||||
return _store(instr, context, instructions, false, 64);
|
||||
},
|
||||
sub: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
subcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
subx: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
subxcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
xor: function(instr, context, instructions) {
|
||||
return Base.xor(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
xorcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
return Base.xor(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
xnor: function(instr, context, instructions) {
|
||||
var ops = [];
|
||||
ops.push(Base.xor(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.not(instr.parsed.opd[2], instr.parsed.opd[2]));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
xnorcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
var ops = [];
|
||||
ops.push(Base.xor(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.not(instr.parsed.opd[2], instr.parsed.opd[2]));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
unimp: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
invalid: function() {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(assembly) {
|
||||
var tokens = assembly.replace(/,/g, ' ').replace(/\[|\]/g, '').replace(/\s+/g, ' ').trim().split(' ');
|
||||
return {
|
||||
mnem: tokens.shift(),
|
||||
opd: tokens
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: null,
|
||||
b: null
|
||||
}
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions) {
|
||||
/* delayed branch fix */
|
||||
for (var i = 0; i < (instructions.length - 1); i++) {
|
||||
var op = instructions[i].parsed.mnem;
|
||||
var n = instructions[i + 1].parsed.mnem;
|
||||
if (_branch_list.indexOf(op) >= 0 && n != 'nop' && _branch_list.indexOf(n) < 0) {
|
||||
Instruction.swap(instructions, i, i + 1);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
},
|
||||
postanalisys: function(instructions, context) {
|
||||
/* simplifies any load address 32/64 bit */
|
||||
for (var i = 0; i < instructions.length; i++) {
|
||||
if (instructions[i].parsed.mnem == 'sethi') {
|
||||
i = sethi32(instructions[i], i, instructions);
|
||||
}
|
||||
}
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,516 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
|
||||
var _common_math = function(e, op, reversed) {
|
||||
if (e.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
if (reversed) {
|
||||
return op(e.opd[1], e.opd[0], e.opd[1]);
|
||||
}
|
||||
return op(e.opd[1], e.opd[1], e.opd[0]);
|
||||
};
|
||||
|
||||
var _compare = function(instr, context) {
|
||||
context.cond.a = instr.parsed.opd[1];
|
||||
context.cond.b = instr.parsed.opd[0];
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, type, zero) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var load_bits = function(register, pointer, bits, signed) {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(pointer, register, bits, signed);
|
||||
};
|
||||
|
||||
var store_bits = function(register, pointer, bits, signed) {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(pointer, register, bits, signed);
|
||||
};
|
||||
|
||||
var _setf_v850_cond = {
|
||||
'lt': {
|
||||
cond: 'LT',
|
||||
arg1: null,
|
||||
},
|
||||
'le': {
|
||||
cond: 'LE',
|
||||
arg1: null,
|
||||
},
|
||||
'ge': {
|
||||
cond: 'GE',
|
||||
arg1: null,
|
||||
},
|
||||
'gt': {
|
||||
cond: 'GT',
|
||||
arg1: null,
|
||||
},
|
||||
'nh': {
|
||||
cond: 'LE',
|
||||
arg1: null,
|
||||
},
|
||||
'h': {
|
||||
cond: 'GT',
|
||||
arg1: null,
|
||||
},
|
||||
'nz': {
|
||||
cond: 'NE',
|
||||
arg1: '0',
|
||||
},
|
||||
'z': {
|
||||
cond: 'EQ',
|
||||
arg1: '0',
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
instructions: {
|
||||
add: function(instr) {
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
and: function(instr) {
|
||||
return _common_math(instr.parsed, Base.and);
|
||||
},
|
||||
b: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
bne: function(instr, context) {
|
||||
return _conditional(instr, context, 'EQ');
|
||||
},
|
||||
beq: function(instr, context) {
|
||||
return _conditional(instr, context, 'NE');
|
||||
},
|
||||
bgt: function(instr, context) {
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
bnh: function(instr, context) {
|
||||
//branch not higher = LE
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bh: function(instr, context) {
|
||||
//branch higher = GT
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
bge: function(instr, context) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
blt: function(instr, context) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
ble: function(instr, context) {
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bz: function(instr, context) {
|
||||
return _conditional(instr, context, 'NE');
|
||||
},
|
||||
bnz: function(instr, context) {
|
||||
return _conditional(instr, context, 'EQ');
|
||||
},
|
||||
cmp: _compare,
|
||||
jarl: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
instr.comments.push('link pointer is lost. (used r0)');
|
||||
} else {
|
||||
instr.comments.push(instr.parsed.opd[1] + ' = PC + 4;');
|
||||
}
|
||||
var arg = instr.parsed.opd[0];
|
||||
if (instr.parsed.opd[0].indexOf('0x') == 0) {
|
||||
arg = Variable.functionPointer(arg);
|
||||
}
|
||||
return Base.call(arg, []);
|
||||
},
|
||||
jmp: function(instr, context, instructions) {
|
||||
if ((instructions.length - 1) == instructions.indexOf(instr)) {
|
||||
//name, args, is_pointer, returns, bits
|
||||
return Base.call(instr.parsed.opd[0], [], true, 'return');
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
jr: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
'ld.b': function(instr) {
|
||||
// ld.X 0x6c36[r4], r14
|
||||
// ["ld.X","0x6c36","r4","r14"]
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 8, true);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 8, true);
|
||||
},
|
||||
'ld.bu': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 8, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 8, false);
|
||||
},
|
||||
'ld.h': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 16, true);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 16, true);
|
||||
},
|
||||
'ld.hu': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 16, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 16, false);
|
||||
},
|
||||
'ld.w': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 32, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 32, false);
|
||||
},
|
||||
mov: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.assign(instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
movea: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.assign(instr.parsed.opd[2], instr.parsed.opd[0]);
|
||||
}
|
||||
if (instr.parsed.opd[0] == '0') {
|
||||
return Base.assign(instr.parsed.opd[2], instr.parsed.opd[1]);
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
movhi: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var a = instr.parsed.opd[0];
|
||||
if (a.indexOf('0x') != 0) {
|
||||
a = '0x' + parseInt(a).toString(16);
|
||||
}
|
||||
return Base.assign(instr.parsed.opd[1], a + '0000');
|
||||
},
|
||||
mul: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
//value, bits, is_signed, is_pointer, is_memory
|
||||
var n = Variable.local(instr.parsed.opd[0], 32, true);
|
||||
return Base.multiply(instr.parsed.opd[2], instr.parsed.opd[1], n);
|
||||
},
|
||||
mulh: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.multiply(instr.parsed.opd[1], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
mulhi: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.multiply(instr.parsed.opd[2], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
not: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.not(instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
nop: function(instr) {
|
||||
return Base.nop();
|
||||
},
|
||||
or: function(instr) {
|
||||
return _common_math(instr.parsed, Base.or);
|
||||
},
|
||||
ori: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.or(instr.parsed.opd[2], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
satadd: function(instr) {
|
||||
// add with previous carry
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
satsub: function(instr) {
|
||||
// add with previous carry
|
||||
return _common_math(instr.parsed, Base.subtract, false);
|
||||
},
|
||||
satsubi: function(instr) {
|
||||
// add with previous carry
|
||||
return _common_math(instr.parsed, Base.subtract, true);
|
||||
},
|
||||
satsubr: function(instr) {
|
||||
// add with previous carry
|
||||
return _common_math(instr.parsed, Base.subtract, true);
|
||||
},
|
||||
shl: function(instr) {
|
||||
return _common_math(instr.parsed, Base.shift_left);
|
||||
},
|
||||
shr: function(instr) {
|
||||
//logically shift right
|
||||
return _common_math(instr.parsed, Base.shift_right);
|
||||
},
|
||||
setf: function(instr, context) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
if (e.opd[0] == 'v' || e.opd[0] == 'nv') {
|
||||
var m = new Variable.macro((e.opd[0] == 'nv' ? '!' : '') + 'IS_OVERFLOW(' + context.cond.a + ', ' + context.cond.b + ')');
|
||||
var op = Base.conditional_assign(e.opd[1], m, null, 'CUST', '1', '0');
|
||||
Global().context.addMarcro('#define IS_OVERFLOW(a,b) (((a<0)&&(b<0)&&(a+b>0))||((a>0)&&(b>0)&&(a+b<0)))');
|
||||
return op;
|
||||
}
|
||||
var o = _setf_v850_cond[e.opd[0]];
|
||||
if (!o) {
|
||||
instr.comments.push('unhandled use-case. please report it.');
|
||||
instr.comments.push(instr.assembly);
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.conditional_assign(e.opd[1], context.cond.a, o.arg1 || context.cond.b, o.cond, '1', '0');
|
||||
},
|
||||
'sld.b': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 8, true);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 8, true);
|
||||
},
|
||||
'sld.bu': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 8, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 8, false);
|
||||
},
|
||||
'sld.h': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 16, true);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 16, true);
|
||||
},
|
||||
'sld.hu': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 16, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 16, false);
|
||||
},
|
||||
'sld.w': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 32, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 32, false);
|
||||
},
|
||||
'sst.b': function(instr) {
|
||||
// st.X , r14, 0x6c36[r4]
|
||||
// ["st.X","r14","0x6c36","r4"]
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 8, true);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 8, true);
|
||||
},
|
||||
'sst.bu': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 8, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 8, false);
|
||||
},
|
||||
'sst.h': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 16, true);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 16, true);
|
||||
},
|
||||
'sst.hu': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 16, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 16, false);
|
||||
},
|
||||
'sst.w': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 32, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 32, false);
|
||||
},
|
||||
'st.b': function(instr) {
|
||||
// st.X , r14, 0x6c36[r4]
|
||||
// ["st.X","r14","0x6c36","r4"]
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 8, true);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 8, true);
|
||||
},
|
||||
'st.bu': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 8, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 8, false);
|
||||
},
|
||||
'st.h': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 16, true);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 16, true);
|
||||
},
|
||||
'st.hu': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 16, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 16, false);
|
||||
},
|
||||
'st.w': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 32, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 32, false);
|
||||
},
|
||||
sub: function(instr) {
|
||||
return _common_math(instr.parsed, Base.subtract);
|
||||
},
|
||||
subr: function(instr) {
|
||||
return _common_math(instr.parsed, Base.subtract, true);
|
||||
},
|
||||
sxb: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.extend(instr.parsed.opd[1], instr.parsed.opd[0], 32);
|
||||
},
|
||||
sxh: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.extend(instr.parsed.opd[1], instr.parsed.opd[0], 32);
|
||||
},
|
||||
tst: function(instr, context, instructions) {
|
||||
var e = instr.parsed;
|
||||
context.cond.a = (e.opd[0] == e.opd[1]) ? e.opd[1] : "(" + e.opd[1] + " & " + e.opd[0] + ")";
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
xor: function(instr) {
|
||||
return _common_math(instr.parsed, Base.xor);
|
||||
},
|
||||
xori: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.xor(instr.parsed.opd[2], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
zxb: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var a = Variable.local(instr.parsed.opd[0], 32, false);
|
||||
return Base.assign(instr.parsed.opd[1], a);
|
||||
},
|
||||
zxh: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var a = Variable.local(instr.parsed.opd[0], 32, false);
|
||||
return Base.assign(instr.parsed.opd[1], a);
|
||||
},
|
||||
invalid: function() {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(assembly) {
|
||||
var ret = assembly.replace(/\[|\]/g, ' ').replace(/,/g, ' ');
|
||||
ret = ret.replace(/\{|\}/g, ' ').replace(/\s+/g, ' ');
|
||||
ret = ret.trim().replace(/\br0\b/g, '0').split(' ');
|
||||
|
||||
return {
|
||||
mnem: ret.shift(),
|
||||
opd: ret
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: '?',
|
||||
b: '?'
|
||||
},
|
||||
leave: false,
|
||||
vars: []
|
||||
};
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,544 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Base from '../core/base.js';
|
||||
import Variable from '../core/variable.js';
|
||||
import Extra from '../core/extra.js';
|
||||
|
||||
var _memory_name = "_memory";
|
||||
|
||||
var _to_argument = function(wasm_var) {
|
||||
return wasm_var.toString('arg');
|
||||
};
|
||||
|
||||
var WasmVar = function(name, type, instr) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.instr = instr;
|
||||
this.toString = function(s) {
|
||||
if (s == 'arg') {
|
||||
return this.type + ' ' + this.name;
|
||||
}
|
||||
return this.name;
|
||||
};
|
||||
};
|
||||
|
||||
var StackVar = function(type, instr) {
|
||||
return new WasmVar(Variable.uniqueName('stack'), type, instr);
|
||||
};
|
||||
|
||||
var _is_next_a_set = function(instr, instructions) {
|
||||
var pos = instructions.indexOf(instr) + 1;
|
||||
if (!instructions[pos]) {
|
||||
return false;
|
||||
}
|
||||
var mnem = instructions[pos].parsed.mnem;
|
||||
return mnem == 'set_local' || mnem == 'set_global' ? pos : -1;
|
||||
};
|
||||
|
||||
var _is_next_local = function(instr, instructions) {
|
||||
var pos = instructions.indexOf(instr) + 1;
|
||||
if (!instructions[pos]) {
|
||||
return false;
|
||||
}
|
||||
var mnem = instructions[pos].parsed.mnem;
|
||||
return mnem == 'set_local' ? pos : -1;
|
||||
};
|
||||
|
||||
var _is_previous_call = function(instr, instructions) {
|
||||
var pos = instructions.indexOf(instr) - 1;
|
||||
if (!instructions[pos]) {
|
||||
return false;
|
||||
}
|
||||
var mnem = instructions[pos].parsed.mnem;
|
||||
return mnem == 'call' ? pos : -1;
|
||||
};
|
||||
|
||||
var _set_local = function(instr, context, instructions, type, allow_args) {
|
||||
var pos = _is_next_local(instr, instructions);
|
||||
var name = allow_args ? 'arg_' : 'local';
|
||||
var n = '';
|
||||
if (pos < 0) {
|
||||
n = instr.parsed.opd[0];
|
||||
if (!context.local[n]) {
|
||||
name += n;
|
||||
context.local[n] = new WasmVar(name, type, instr);
|
||||
if (allow_args) {
|
||||
context.input[n] = context.local[n];
|
||||
}
|
||||
}
|
||||
return context.local[n];
|
||||
}
|
||||
n = instructions[pos].parsed.opd[0];
|
||||
if (!context.local[n]) {
|
||||
name += n;
|
||||
context.local[n] = new WasmVar(name, type, instr);
|
||||
if (allow_args) {
|
||||
context.input[n] = context.local[n];
|
||||
}
|
||||
}
|
||||
return context.local[n];
|
||||
};
|
||||
|
||||
var _set_global = function(instr, context, instructions, type) {
|
||||
var n = instr.parsed.opd[0].trim();
|
||||
if (!context.global[n]) {
|
||||
context.global[n] = new WasmVar('global_' + instr.parsed.opd[0], type, instr);
|
||||
}
|
||||
return context.global[n];
|
||||
};
|
||||
|
||||
var _bits = function(o) {
|
||||
if (typeof o == 'object') {
|
||||
o = o.parsed.type;
|
||||
}
|
||||
switch (o) {
|
||||
case 'f32':
|
||||
return 32;
|
||||
case 'f64':
|
||||
return 64;
|
||||
case 'i64':
|
||||
return 64;
|
||||
default:
|
||||
return 32;
|
||||
}
|
||||
};
|
||||
|
||||
var _type = function(o, unsigned) {
|
||||
if (typeof o == 'object') {
|
||||
o = o.parsed.type;
|
||||
}
|
||||
switch (o) {
|
||||
case 'f32':
|
||||
return 'float';
|
||||
case 'f64':
|
||||
return 'double';
|
||||
case 'i64':
|
||||
return unsigned ? 'uint64_t' : 'int64_t';
|
||||
default:
|
||||
return unsigned ? 'uint32_t' : 'int32_t';
|
||||
}
|
||||
};
|
||||
|
||||
var _remove_const = function(instr, instructions, stackval) {
|
||||
if (stackval && instructions.indexOf(stackval.instr) == (instructions.indexOf(instr) - 1) && stackval.instr.parsed.mnem == 'const') {
|
||||
stackval.instr.valid = false;
|
||||
// <type>.const <N>
|
||||
return stackval.instr.parsed.opd[0];
|
||||
}
|
||||
return stackval;
|
||||
};
|
||||
|
||||
var _math = function(instr, context, instructions, type, op) {
|
||||
var source_b = context.stack.pop();
|
||||
source_b = _remove_const(instr, instructions, source_b);
|
||||
var source_a = context.stack.pop();
|
||||
source_a.type = type;
|
||||
source_b.type = type;
|
||||
var pos = _is_next_a_set(instr, instructions);
|
||||
var destination = StackVar(type, instr);
|
||||
context.stack.push(destination); // push must happen.
|
||||
if (pos < 0) {
|
||||
return op(destination.toString(), source_a.toString(), source_b.toString());
|
||||
}
|
||||
var next_set = instructions[pos];
|
||||
next_set.valid = false;
|
||||
if (next_set.parsed.mnem == 'set_local') {
|
||||
destination = _set_local(next_set, context, instructions, 'int32_t', false);
|
||||
} else {
|
||||
destination = _set_global(next_set, context, instructions, 'int32_t');
|
||||
}
|
||||
return op(destination.toString(), source_a.toString(), source_b.toString());
|
||||
};
|
||||
|
||||
var _cmp = {
|
||||
eq: 'EQ',
|
||||
ne: 'NE',
|
||||
eqz: 'EQ',
|
||||
nez: 'NE',
|
||||
gt_s: 'GT',
|
||||
gt_u: 'GT',
|
||||
ge_s: 'GE',
|
||||
ge_u: 'GE',
|
||||
lt_s: 'LT',
|
||||
lt_u: 'LT',
|
||||
le_s: 'LE',
|
||||
le_u: 'LE',
|
||||
gt: 'GT',
|
||||
ge: 'GE',
|
||||
lt: 'LT',
|
||||
le: 'LE'
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, instructions) {
|
||||
var cond = {};
|
||||
cond.b = context.stack.pop().toString();
|
||||
cond.a = context.stack.pop().toString();
|
||||
if (!cond.a) {
|
||||
cond.a = '?';
|
||||
}
|
||||
if (!cond.b) {
|
||||
cond.b = '?';
|
||||
}
|
||||
cond.cmp = _cmp[instr.parsed.mnem];
|
||||
context.condstack.push(cond);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _conditional_zero = function(instr, context, instructions) {
|
||||
var cond = {};
|
||||
cond.b = '0';
|
||||
cond.a = context.stack.pop().toString();
|
||||
if (!cond.a) {
|
||||
cond.a = '?';
|
||||
}
|
||||
cond.cmp = _cmp[instr.parsed.mnem];
|
||||
context.condstack.push(cond);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _set_instruction_conditional = function(instr, context) {
|
||||
var cond = context.condstack.pop();
|
||||
if (!cond) {
|
||||
var arg = context.stack.pop();
|
||||
if (!arg) {
|
||||
arg = '?';
|
||||
}
|
||||
instr.conditional(arg.toString(), '0', 'NE');
|
||||
} else {
|
||||
instr.conditional(cond.a, cond.b, cond.cmp);
|
||||
}
|
||||
};
|
||||
|
||||
var _common_load = function(instr, context) {
|
||||
context.memory = true;
|
||||
var offset = instr.parsed.opd[instr.parsed.opd.length - 1];
|
||||
var pointer = context.stack.pop().toString();
|
||||
var register = StackVar(_type(instr), instr);
|
||||
context.stack.push(register);
|
||||
if (offset != '0') {
|
||||
var bits = instr.parsed.mnem.match(/\d+/) || ["32"];
|
||||
bits = parseInt(bits[0]) / 8;
|
||||
pointer += ' + ' + (parseInt(offset) / bits).toString();
|
||||
}
|
||||
pointer = _memory_name + ' + ' + pointer;
|
||||
return Base.read_memory(pointer, register.toString(), _bits(instr), instr.parsed.mnem.endsWith('_s'));
|
||||
};
|
||||
|
||||
var _common_store = function(instr, context) {
|
||||
context.memory = true;
|
||||
var offset = instr.parsed.opd[instr.parsed.opd.length - 1];
|
||||
var register = context.stack.pop().toString();
|
||||
var pointer = context.stack.pop().toString();
|
||||
var bits = instr.parsed.mnem.match(/\d+/) || ["32"];
|
||||
bits = parseInt(bits[0]);
|
||||
if (offset != '0') {
|
||||
pointer += ' + ' + (parseInt(offset) / (bits / 8)).toString();
|
||||
}
|
||||
pointer = _memory_name + ' + ' + pointer;
|
||||
return Base.write_memory(pointer, register.toString(), bits, false);
|
||||
};
|
||||
|
||||
var _wasm_arch = {
|
||||
instructions: {
|
||||
eq: _conditional,
|
||||
ne: _conditional,
|
||||
eqz: _conditional_zero,
|
||||
nez: _conditional_zero,
|
||||
gt_s: _conditional,
|
||||
gt_u: _conditional,
|
||||
ge_s: _conditional,
|
||||
ge_u: _conditional,
|
||||
lt_s: _conditional,
|
||||
lt_u: _conditional,
|
||||
le_s: _conditional,
|
||||
le_u: _conditional,
|
||||
gt: _conditional,
|
||||
ge: _conditional,
|
||||
lt: _conditional,
|
||||
le: _conditional,
|
||||
const: function(instr, context, instructions) {
|
||||
var s = StackVar('const ' + _type(instr), instr);
|
||||
context.stack.push(s);
|
||||
var num = parseInt(instr.parsed.opd[0]);
|
||||
if (num > 1023) {
|
||||
num = '0x' + num.toString(16);
|
||||
}
|
||||
return Base.assign(s.toString(), num.toString());
|
||||
},
|
||||
load: _common_load,
|
||||
load8_s: _common_load,
|
||||
load8_u: _common_load,
|
||||
load16_s: _common_load,
|
||||
load16_u: _common_load,
|
||||
store: _common_store,
|
||||
store8: _common_store,
|
||||
store16: _common_store,
|
||||
store32: _common_store,
|
||||
add: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.add);
|
||||
},
|
||||
and: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.and);
|
||||
},
|
||||
div_s: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.divide);
|
||||
},
|
||||
div_u: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr, true), Base.divide);
|
||||
},
|
||||
extend_s: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr));
|
||||
},
|
||||
extend_u: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr, true));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr, true));
|
||||
},
|
||||
mul: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.multiply);
|
||||
},
|
||||
or: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.or);
|
||||
},
|
||||
reinterpret: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.assign(s.toString('arg'), b.toString());
|
||||
}
|
||||
return Base.assign(s.toString(), b.toString());
|
||||
},
|
||||
rem_s: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.module);
|
||||
},
|
||||
rem_u: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr, true), Base.module);
|
||||
},
|
||||
shl: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.shift_left);
|
||||
},
|
||||
shr_s: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.shift_right);
|
||||
},
|
||||
shr_u: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr, true), Base.shift_right);
|
||||
},
|
||||
sub: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.subtract);
|
||||
},
|
||||
wrap: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr, true));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr, true));
|
||||
},
|
||||
trunc_s: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr, true));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr, true));
|
||||
},
|
||||
trunc_u: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr, true));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr, true));
|
||||
},
|
||||
xor: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.xor);
|
||||
},
|
||||
get_global: function(instr, context, instructions) {
|
||||
var dst = _set_global(instr, context, instructions, 'int32_t');
|
||||
context.stack.push(dst);
|
||||
return Base.nop();
|
||||
},
|
||||
set_global: function(instr, context, instructions) {
|
||||
var dst = _set_global(instr, context, instructions, 'int32_t');
|
||||
var arg = context.stack.pop();
|
||||
return Base.assign(dst.toString(), arg ? arg.toString() : "?");
|
||||
},
|
||||
get_local: function(instr, context, instructions) {
|
||||
context.stack.push(_set_local(instr, context, instructions, 'int32_t', true));
|
||||
return Base.nop();
|
||||
},
|
||||
set_local: function(instr, context, instructions) {
|
||||
var dst = _set_local(instr, context, instructions, 'int32_t', false);
|
||||
var pos = _is_previous_call(instr, instructions);
|
||||
if (pos > -1) {
|
||||
var call = instructions[pos];
|
||||
call.code = Base.assign(dst.toString(), call.code);
|
||||
return Base.nop();
|
||||
}
|
||||
var arg = context.stack.pop();
|
||||
return Base.assign(dst.toString(), arg ? arg.toString() : "?");
|
||||
},
|
||||
tee_local: function(instr, context, instructions) {
|
||||
var dst = _set_local(instr, context, instructions, 'int32_t', true);
|
||||
var pos = _is_previous_call(instr, instructions);
|
||||
if (pos > -1) {
|
||||
var call = instructions[pos];
|
||||
call.code = Base.assign(dst.toString(), call.code);
|
||||
return Base.nop();
|
||||
}
|
||||
var arg = context.stack[context.stack.length - 1];
|
||||
return Base.assign(dst.toString(), arg.toString());
|
||||
},
|
||||
drop: function(instr, context, instructions) {
|
||||
_remove_const(instr, instructions, context.stack.pop());
|
||||
return Base.nop();
|
||||
},
|
||||
call: function(instr, context, instructions) {
|
||||
var args = [];
|
||||
for (var i = instructions.indexOf(instr) - 1; i >= 0; i--) {
|
||||
var previous = instructions[i].parsed.mnem;
|
||||
if (!previous.startsWith('get_') && previous != 'const') {
|
||||
break;
|
||||
}
|
||||
args.unshift(context.stack.pop());
|
||||
}
|
||||
if (instr.parsed.opd[0].match(/^\d+$/)) {
|
||||
return Base.call('fcn_' + instr.parsed.opd[0], args);
|
||||
}
|
||||
return Base.call(instr.parsed.opd[0], args);
|
||||
},
|
||||
return: function(instr, context, instructions) {
|
||||
var ret = null;
|
||||
if (context.stack.length > 0) {
|
||||
if (context.stack.length > 1) {
|
||||
Global().warning('[wasm] stack len is not zero: ' + context.stack.length);
|
||||
}
|
||||
context.returned = context.stack.pop();
|
||||
ret = _remove_const(instr, instructions, context.returned);
|
||||
if (!Extra.is.string(ret)) {
|
||||
ret = context.returned.toString();
|
||||
}
|
||||
}
|
||||
return Base.return(ret);
|
||||
},
|
||||
'if': function(instr, context, instructions) {
|
||||
_set_instruction_conditional(instr, context);
|
||||
return Base.nop();
|
||||
},
|
||||
br_if: function(instr, context, instructions) {
|
||||
_set_instruction_conditional(instr, context);
|
||||
return Base.nop();
|
||||
},
|
||||
'else': function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
block: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
loop: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
end: function(instr, context, instructions) {
|
||||
if (instr.jump && instr.jump.lte(instr.location)) {
|
||||
_set_instruction_conditional(instr, context);
|
||||
} else {
|
||||
var curidx = instructions.indexOf(instr);
|
||||
if (curidx == (instructions.length - 1)) {
|
||||
var ret = null;
|
||||
if (context.stack.length > 0) {
|
||||
if (context.stack.length > 1) {
|
||||
Global().warning('[wasm] stack len is not zero: ' + context.stack.length);
|
||||
}
|
||||
context.returned = context.stack.pop();
|
||||
ret = _remove_const(instr, instructions, context.returned);
|
||||
if (!Extra.is.string(ret)) {
|
||||
ret = ret.toString();
|
||||
}
|
||||
}
|
||||
return Base.return(ret);
|
||||
}
|
||||
}
|
||||
|
||||
return Base.nop();
|
||||
},
|
||||
br: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
nop: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
invalid: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(asm) {
|
||||
asm = asm.trim().replace(/\s+/g, ' ').replace(/\//g, ' ');
|
||||
var type = asm.match(/[if]\d\d\./);
|
||||
if (type) {
|
||||
type = type[0];
|
||||
asm = asm.replace(/[if]\d\d\./, '');
|
||||
}
|
||||
asm = asm.split(' ');
|
||||
|
||||
return {
|
||||
type: type || 'i32',
|
||||
mnem: asm.shift(),
|
||||
opd: asm
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
returned: null,
|
||||
global: {},
|
||||
input: {},
|
||||
local: {},
|
||||
stack: [],
|
||||
condstack: [],
|
||||
memory: false,
|
||||
};
|
||||
},
|
||||
globalvars: function(context) {
|
||||
var to_return = Extra.to.array(context.global).map(_to_argument);
|
||||
if (context.memory) {
|
||||
to_return.unshift("extern uint8_t " + _memory_name + "[]");
|
||||
}
|
||||
return to_return;
|
||||
},
|
||||
localvars: function(context) {
|
||||
var vars = [];
|
||||
for (var k in context.local) {
|
||||
var o = context.local[k];
|
||||
if (Extra.is.inObject(context.input, o) || Extra.is.inObject(context.global, o)) {
|
||||
continue;
|
||||
}
|
||||
vars.push(o.toString('arg'));
|
||||
}
|
||||
return vars;
|
||||
},
|
||||
arguments: function(context) {
|
||||
return Extra.to.array(context.input).map(_to_argument);
|
||||
},
|
||||
returns: function(context) {
|
||||
if (context.returned) {
|
||||
return context.returned.type.replace(/const\s/, '');
|
||||
}
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
|
||||
export default _wasm_arch;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,34 @@
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import _6502 from './arch/6502.js';
|
||||
import _8051 from './arch/8051.js';
|
||||
import arm from './arch/arm.js';
|
||||
import avr from './arch/avr.js';
|
||||
import dalvik from './arch/dalvik.js';
|
||||
import m68k from './arch/m68k.js';
|
||||
import mips from './arch/mips.js';
|
||||
import ppc from './arch/ppc.js';
|
||||
import riscv from './arch/riscv.js';
|
||||
import sh from './arch/sh.js';
|
||||
import sparc from './arch/sparc.js';
|
||||
import v850 from './arch/v850.js';
|
||||
import wasm from './arch/wasm.js';
|
||||
import x86 from './arch/x86.js';
|
||||
|
||||
export default {
|
||||
'6502': _6502,
|
||||
'8051': _8051,
|
||||
arm: arm,
|
||||
avr: avr,
|
||||
dalvik: dalvik,
|
||||
m68k: m68k,
|
||||
mips: mips,
|
||||
ppc: ppc,
|
||||
riscv: riscv,
|
||||
sh: sh,
|
||||
sparc: sparc,
|
||||
v850: v850,
|
||||
wasm: wasm,
|
||||
x86: x86
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
var __colors = {
|
||||
black: [30, 39],
|
||||
red: [31, 39],
|
||||
green: [32, 39],
|
||||
yellow: [33, 39],
|
||||
blue: [34, 39],
|
||||
magenta: [35, 39],
|
||||
cyan: [36, 39],
|
||||
white: [37, 39],
|
||||
gray: [90, 39],
|
||||
};
|
||||
|
||||
function pair(name, n) {
|
||||
if (name.length === 6) {
|
||||
n *= 2;
|
||||
return parseInt(name.substring(n, n + 2), 16);
|
||||
}
|
||||
return (parseInt(name.substring(n, n + 1), 16) << 4) >>> 0;
|
||||
}
|
||||
var Color = function(name) {
|
||||
var fn = (function(){
|
||||
var that = function(x) {
|
||||
return that.open + x + that.close;
|
||||
};
|
||||
return that;
|
||||
})();
|
||||
if (name.startsWith('rgb:')) {
|
||||
name = name.substring(4);
|
||||
const str = '38;2;' + pair(name, 0) + ';' + pair(name, 1) + ';' + pair(name, 2);
|
||||
fn.open = '\u001b[' + str + 'm';
|
||||
fn.close = '\u001b[39m';
|
||||
} else {
|
||||
if (!__colors[name]) {
|
||||
throw new Error('Invalid name: ' + name);
|
||||
}
|
||||
fn.open = '\u001b[' + __colors[name][0] + 'm';
|
||||
fn.close = '\u001b[' + __colors[name][1] + 'm';
|
||||
}
|
||||
return fn;
|
||||
};
|
||||
Color.make = function(theme) {
|
||||
var g = {};
|
||||
for (var key in theme) {
|
||||
g[key] = Color(theme[key]);
|
||||
}
|
||||
return g;
|
||||
};
|
||||
export default Color;
|
||||
@@ -0,0 +1,17 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
var Color = function(name) {
|
||||
var fn = function(x) {
|
||||
return x;
|
||||
};
|
||||
return fn;
|
||||
};
|
||||
Color.make = function(theme) {
|
||||
var g = {};
|
||||
for (var key in theme) {
|
||||
g[key] = Color(theme[key]);
|
||||
}
|
||||
return g;
|
||||
};
|
||||
export default Color;
|
||||
@@ -0,0 +1,167 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
export default function() {
|
||||
this.lines = [];
|
||||
this.errors = [];
|
||||
this.log = [];
|
||||
|
||||
/**
|
||||
* Print a line of decompiled code.
|
||||
* @param str - content of the line
|
||||
* @param offset - offset of the original instruction (optional)
|
||||
*/
|
||||
this.printLine = function(str, offset) {
|
||||
var line = { str: str, offset: offset };
|
||||
this.lines.push(line);
|
||||
};
|
||||
|
||||
/**
|
||||
* Print a line for logging.
|
||||
* @param str - content to print
|
||||
* @param error - boolean whether this is an error (optional)
|
||||
*/
|
||||
this.printLog = function(str, error) {
|
||||
if (Global().evars.extra.json) {
|
||||
if(error) {
|
||||
this.errors.push(str);
|
||||
} else {
|
||||
this.log.push(str);
|
||||
}
|
||||
} else {
|
||||
console.log(str);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal C macro list.
|
||||
* @type {Array}
|
||||
*/
|
||||
this.macros = ['#include <stdint.h>'];
|
||||
|
||||
/**
|
||||
* Adds a C macro string to the global list of macros.
|
||||
* @param {String} - item to add
|
||||
*/
|
||||
this.addMacro = function(x) {
|
||||
if (this.macros.indexOf(x) < 0) {
|
||||
this.macros.push(x);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* prints all the macros. used internally by core.js
|
||||
*/
|
||||
this.printMacros = function(offset) {
|
||||
if (!Global().evars.honor.blocks) {
|
||||
var t = Global().printer.theme;
|
||||
for (var i = 0; i < this.macros.length; i++) {
|
||||
this.printLine(this.identfy() + t.macro(this.macros[i]), offset);
|
||||
}
|
||||
}
|
||||
this.printLine(this.identfy() + ' ', offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* List of dependencies (mostly generic functions like rotate, etc..)
|
||||
* that cannot be described easily from a asm opcode.
|
||||
* @type {Array}
|
||||
*/
|
||||
this.dependencies = [];
|
||||
|
||||
/**
|
||||
* adds a dependency to the global list.
|
||||
* @param {String|Object} - item to add
|
||||
*/
|
||||
this.addDependency = function(x) {
|
||||
if (this.dependencies.indexOf(x) < 0) {
|
||||
this.dependencies.push(x);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* prints all the dependencies. used internally by core.js
|
||||
*/
|
||||
this.printDependencies = function(offset) {
|
||||
if (Global().evars.honor.blocks) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dependencies.forEach(function(x) {
|
||||
x.print(offset);
|
||||
});
|
||||
if (this.dependencies.length > 0) {
|
||||
this.printLine(this.identfy() + ' ', offset);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Assembly identation "constant"
|
||||
* @type {String}
|
||||
*/
|
||||
this.identAsm = '';
|
||||
|
||||
/**
|
||||
* Sets the max size of the identation line for the `--assembly/jsdec.asm` option.
|
||||
* @param {Number} size - Size to set
|
||||
*/
|
||||
this.identAsmSet = function(size) {
|
||||
// size = 0x + addr + space + asm + space
|
||||
size += 10;
|
||||
while (this.identAsm.length < size) {
|
||||
this.identAsm += ' ';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare the max size of the identation line with the given one.
|
||||
* @param {Number} size - Size to set
|
||||
* @return {Number}
|
||||
*/
|
||||
this.identCompare = function(size) {
|
||||
// size = 0x + addr + space + asm + space
|
||||
return size - ((this.identAsm.length / 4) - 10);
|
||||
};
|
||||
|
||||
/**
|
||||
* Scope identation
|
||||
* @type {String}
|
||||
*/
|
||||
this.ident = '';
|
||||
|
||||
/**
|
||||
* Adds spaces per each scope going in.
|
||||
*/
|
||||
this.identIn = function() {
|
||||
this.ident += ' ';
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes spaces per each scope going out.
|
||||
*/
|
||||
this.identOut = function(force) {
|
||||
this.ident = this.ident.substr(4, this.ident.length);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the data to be used for line identation.
|
||||
* It's used mostly internally when a line is going to be printed on screen.
|
||||
* @param {Number} size_no_colors - Size of the strings without ansi colors
|
||||
* @param {String} string_to_print - String to be printed before the pipe
|
||||
* @param {Boolean} disable_identation - Force to not set the scope identation of the line.
|
||||
* @return {string} - Identation including eventual string to be printed.
|
||||
*/
|
||||
this.identfy = function(size_no_colors, string_to_print, disable_identation) {
|
||||
var ident = disable_identation ? '' : this.ident;
|
||||
if (Global().evars.honor.assembly && !Global().evars.honor.blocks) {
|
||||
string_to_print = string_to_print || '';
|
||||
size_no_colors = size_no_colors || 0;
|
||||
return ' ' + string_to_print + this.identAsm.substring(size_no_colors, this.identAsm.length) + ' | ' + ident;
|
||||
} else if (Global().evars.honor.offsets && !Global().evars.honor.blocks) {
|
||||
string_to_print = string_to_print || '';
|
||||
size_no_colors = size_no_colors || 0;
|
||||
return ' ' + string_to_print + this.identAsm.substring(size_no_colors, 14) + ' | ' + ident;
|
||||
}
|
||||
return ident;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Base from './core/base.js';
|
||||
import Block from './core/block.js';
|
||||
import Scope from './core/scope.js';
|
||||
import Extra from './core/extra.js';
|
||||
import Strings from './core/strings.js';
|
||||
import Symbols from './core/symbols.js';
|
||||
import Classes from './core/classes.js';
|
||||
import Functions from './core/functions.js';
|
||||
import Instruction from './core/instruction.js';
|
||||
import ControlFlow from './core/controlflow.js';
|
||||
import XRefs from './core/xrefs.js';
|
||||
|
||||
/**
|
||||
* Fixes for known routine names that are standard (like main)
|
||||
* @param {String} routine_name Routine name
|
||||
* @param {String} return_type Return type
|
||||
* @return {String} New return type
|
||||
*/
|
||||
var _hardcoded_fixes = function(routine_name, return_type) {
|
||||
if (Extra.replace.call(routine_name) == "main") {
|
||||
return 'int32_t';
|
||||
}
|
||||
return return_type || 'void';
|
||||
};
|
||||
|
||||
/**
|
||||
* Is the function that is called after the opcode analisys.
|
||||
* Essentially analyze the flows and allows the call of
|
||||
* the `postanalisys` function that has to be set in the architecture.
|
||||
* @param {Object} session - Current session object.
|
||||
* @param {Object} arch - Current architecture object
|
||||
* @param {Object} arch_context - Current architecture context object.
|
||||
*/
|
||||
var _post_analysis = function(session, arch, arch_context) {
|
||||
ControlFlow(session);
|
||||
if (arch.postanalisys) {
|
||||
arch.postanalisys(session.instructions, arch_context);
|
||||
}
|
||||
var routine_name = arch.routine_name ? arch.routine_name(session.routine_name) : Extra.replace.call(session.routine_name);
|
||||
if (session.instructions.length < 1) {
|
||||
return;
|
||||
}
|
||||
var routine = new Scope.routine(session.instructions[0].location, {
|
||||
returns: _hardcoded_fixes(session.routine_name, arch.returns(arch_context)),
|
||||
name: session.routine_name,
|
||||
routine_name: routine_name,
|
||||
args: arch.arguments(arch_context) || [],
|
||||
locals: arch.localvars(arch_context) || [],
|
||||
globals: arch.globalvars(arch_context) || []
|
||||
});
|
||||
|
||||
session.routine = routine;
|
||||
};
|
||||
|
||||
/**
|
||||
* Is the function that is called before the opcode analisys.
|
||||
* Calls `preanalisys` function that has to be set in the architecture
|
||||
* and copies the instruction into the first block and updates the bounds of this.
|
||||
* @param {Object} session - Current session object.
|
||||
* @param {Object} arch - Current architecture object
|
||||
* @param {Object} arch_context - Current architecture context object.
|
||||
*/
|
||||
var _pre_analysis = function(session, arch, arch_context) {
|
||||
if (arch.preanalisys) {
|
||||
arch.preanalisys(session.instructions, arch_context);
|
||||
}
|
||||
session.blocks[0].instructions = session.instructions.slice();
|
||||
session.blocks[0].update();
|
||||
};
|
||||
|
||||
/**
|
||||
* Most important of the analisys block: it analize the architecture opcodes.
|
||||
* @param {Object} session - Current session object.
|
||||
* @param {Object} arch - Current architecture object
|
||||
* @param {Object} arch_context - Current architecture context object.
|
||||
*/
|
||||
var _decompile = function(session, arch, arch_context) {
|
||||
var instructions = session.blocks[0].instructions;
|
||||
for (var i = 0; i < instructions.length; i++) {
|
||||
var instr = instructions[i];
|
||||
if (!instr.parsed.mnem || instr.parsed.mnem.length < 1) {
|
||||
Global().warning("invalid mnem. stopping instruction analysis.");
|
||||
break;
|
||||
}
|
||||
var fcn = arch.instructions[instr.parsed.mnem];
|
||||
// Global().context.printLine(instr.assembly)
|
||||
instr.code = fcn ? fcn(instr, arch_context, instructions) : new Base.unknown(instr.assembly);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Prints the current session into the screen.
|
||||
* @param {Object} session - Current session object.
|
||||
*/
|
||||
var _print = function(session) {
|
||||
var lineoff = session.instructions.length > 0 ? session.instructions[0].location : null;
|
||||
if (!session.routine) {
|
||||
Global().context.printLog('Error: no "good" data given (all invalid opcodes).', true);
|
||||
return;
|
||||
}
|
||||
if (Global().evars.extra.ascomment) {
|
||||
session.ascomment();
|
||||
Global().context.printLog('[jsdec] comments applied for "' + session.routine_name + '".');
|
||||
return;
|
||||
} else if (Global().evars.extra.ascodeline) {
|
||||
session.ascodeline();
|
||||
Global().context.printLog('[jsdec] new code lines applied for "' + session.routine_name + '".');
|
||||
return;
|
||||
}
|
||||
if (!Global().evars.extra.allfunctions) {
|
||||
var t = Global().printer.theme;
|
||||
var asm_header = Global().evars.honor.offsets ? '' : '; assembly';
|
||||
var details = '/* ' + Global().evars.extra.file + ' @ 0x' + Global().evars.extra.offset.toString(16) + ' */';
|
||||
|
||||
Global().context.printLine(Global().context.identfy(asm_header.length, t.comment(asm_header)) + t.comment('/* jsdec pseudo code output */'), lineoff);
|
||||
Global().context.printLine(Global().context.identfy() + t.comment(details), lineoff);
|
||||
if (['java', 'dalvik'].indexOf(Global().evars.arch) < 0) {
|
||||
Global().context.printMacros(lineoff);
|
||||
Global().context.printDependencies(lineoff);
|
||||
}
|
||||
}
|
||||
session.print();
|
||||
lineoff = session.instructions.length > 0 ? session.instructions[session.instructions.length - 1].location : null;
|
||||
while (Global().context.ident.length > 0) {
|
||||
Global().context.identOut();
|
||||
var value = Global().context.identfy() + '}';
|
||||
Global().context.printLine(value, lineoff);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines the structure that will be used as session for analisys steps.
|
||||
* @param {Object} data - Data to be analized.
|
||||
* @param {Object} arch - Current architecture object
|
||||
*/
|
||||
var _session = function(data, arch) {
|
||||
this.blocks = [new Block()];
|
||||
var instructions = [];
|
||||
var symbols = new Symbols(data.xrefs.symbols);
|
||||
var strings = new Strings(data.xrefs.strings);
|
||||
var classes = new Classes(data.xrefs.classes);
|
||||
var functions = new Functions(data.xrefs.functions);
|
||||
var max_length = 0;
|
||||
var max_address = 8;
|
||||
Global().xrefs = new XRefs(strings, symbols, classes);
|
||||
for (var i = 0; i < data.graph[0].blocks.length; i++) {
|
||||
var block = data.graph[0].blocks[i];
|
||||
// This is hacky but it is required by wasm..
|
||||
if (data.arch == 'wasm') {
|
||||
var last = block.ops[block.ops.length - 1];
|
||||
if (!last.jump) {
|
||||
last.jump = block.jump;
|
||||
}
|
||||
}
|
||||
instructions = instructions.concat(block.ops.filter(function(b) {
|
||||
return b.opcode != null;
|
||||
}).map(function(b) {
|
||||
if (max_length < b.opcode.length) {
|
||||
max_length = b.opcode.length;
|
||||
}
|
||||
var ins = new Instruction(b, arch, i);
|
||||
if (max_address < ins.location.toString(16)) {
|
||||
max_address = ins.location.toString(16).length;
|
||||
}
|
||||
ins.symbol = symbols.search(ins.pointer || ins.jump);
|
||||
ins.string = strings.search(ins.pointer);
|
||||
ins.klass = classes.search(ins.pointer);
|
||||
ins.callee = functions.search(ins.jump);
|
||||
return ins;
|
||||
}));
|
||||
}
|
||||
this.routine_name = data.graph[0].name;
|
||||
if (max_length < this.routine_name.length) {
|
||||
max_length = this.routine_name.length;
|
||||
}
|
||||
Global().context.identAsmSet(max_address + (max_length < this.routine_name.length ? this.routine_name.length : max_length));
|
||||
this.instructions = instructions.filter(function(op, p, ops) {
|
||||
for (var i = p - 1; i >= 0; i--) {
|
||||
if (ops[i].location.eq(op.location)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
this.ascomment = function() {
|
||||
for (var i = 0; i < this.blocks.length; i++) {
|
||||
this.blocks[i].ascomment();
|
||||
}
|
||||
};
|
||||
this.ascodeline = function() {
|
||||
for (var i = 0; i < this.blocks.length; i++) {
|
||||
this.blocks[i].ascodeline();
|
||||
}
|
||||
};
|
||||
this.print = function() {
|
||||
this.routine.print();
|
||||
for (var i = 0; i < this.blocks.length; i++) {
|
||||
this.blocks[i].print();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
decompile: _decompile,
|
||||
session: _session,
|
||||
analysis: {
|
||||
pre: _pre_analysis,
|
||||
post: _post_analysis
|
||||
},
|
||||
print: _print,
|
||||
};
|
||||
@@ -0,0 +1,522 @@
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2018-2019 elicn
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Cpp from '../db/cpp.js';
|
||||
import CCalls from '../db/c_calls.js';
|
||||
import Extra from './extra.js';
|
||||
import Variable from './variable.js';
|
||||
import Condition from './condition.js';
|
||||
import Long from '../long.js';
|
||||
|
||||
/**
|
||||
* Wraps a string with parenthesis.
|
||||
* @param {string} s A string to wrap
|
||||
* @returns {string} `s` wrapped by parenthesis
|
||||
*/
|
||||
var parenthesize = function(s) {
|
||||
return ['(', s, ')'].join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps a string with parenthesis only if it is complex.
|
||||
* @param {string} s A string to wrap
|
||||
* @returns {string} `s` wrapped by parenthesis if `s` is a complex string, and `s` otherwise
|
||||
*/
|
||||
var autoParen = function(s) {
|
||||
return (s.indexOf(' ') > (-1) ? parenthesize(s) : s);
|
||||
};
|
||||
|
||||
var autoString = function(v) {
|
||||
v = v || "";
|
||||
return Extra.is.string(v) ? Global().printer.auto(v) : v.toString();
|
||||
};
|
||||
|
||||
var _generic_asm = function(asm) {
|
||||
this.asm = asm;
|
||||
|
||||
this.toString = function() {
|
||||
return Global().printer.theme.callname('__asm') + ' (' + Global().printer.theme.text('"' + this.asm + '"') + ')';
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Unary expression
|
||||
* @constructor
|
||||
* @inner
|
||||
*/
|
||||
var _uexpr = function(operator, operand) {
|
||||
this.operator = operator;
|
||||
|
||||
this.operands = [
|
||||
autoString(operand)
|
||||
];
|
||||
|
||||
/** @returns {!string} */
|
||||
this.toString = function() {
|
||||
return [
|
||||
this.operator,
|
||||
this.operands[0]
|
||||
].join('');
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Unary expression with postfix notation
|
||||
* @constructor
|
||||
* @inner
|
||||
*/
|
||||
var _uexpr_pf = function(operator, operand) {
|
||||
_uexpr.call(this, operator, [operand]);
|
||||
|
||||
/** @returns {!string} */
|
||||
this.toString = function() {
|
||||
return [
|
||||
this.operands[0],
|
||||
this.operator,
|
||||
].join('');
|
||||
};
|
||||
};
|
||||
|
||||
_uexpr_pf.prototype = Object.create(_uexpr.prototype);
|
||||
|
||||
/**
|
||||
* Binary expression
|
||||
* @constructor
|
||||
* @inner
|
||||
*/
|
||||
var _bexpr = function(operator, lhand, rhand) {
|
||||
this.operator = operator;
|
||||
|
||||
this.operands = [
|
||||
autoString(lhand),
|
||||
autoString(rhand)
|
||||
];
|
||||
|
||||
/** @returns {!string} */
|
||||
this.toString = function() {
|
||||
return [
|
||||
this.operands[0],
|
||||
this.operator,
|
||||
this.operands[1]
|
||||
].join(' ');
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Ternary expression
|
||||
* @constructor
|
||||
* @inner
|
||||
*/
|
||||
var _texpr = function(operator1, operator2, operand1, operand2, operand3) {
|
||||
this.operators = [operator1, operator2];
|
||||
|
||||
this.operands = [
|
||||
autoParen(autoString(operand1)),
|
||||
autoParen(autoString(operand2)),
|
||||
autoParen(autoString(operand3))
|
||||
];
|
||||
|
||||
/** @returns {!string} */
|
||||
this.toString = function() {
|
||||
return [
|
||||
this.operands[0],
|
||||
this.operators[0],
|
||||
this.operands[1],
|
||||
this.operators[1],
|
||||
this.operands[2]
|
||||
].join(' ');
|
||||
};
|
||||
};
|
||||
|
||||
var _assign = function(lhand, rhand) {
|
||||
if (lhand == rhand) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ((rhand instanceof _bexpr) && (autoString(lhand) == rhand.operands[0])) {
|
||||
return new _bexpr(rhand.operator + '=', lhand, rhand.operands[1]);
|
||||
}
|
||||
|
||||
if (rhand instanceof Variable.functionPointer) {
|
||||
rhand = parenthesize(rhand);
|
||||
}
|
||||
|
||||
return new _bexpr('=', lhand, rhand);
|
||||
};
|
||||
|
||||
var _cast = function(source, type) {
|
||||
return new _uexpr(parenthesize(Global().printer.theme.types(type)) + ' ', source);
|
||||
};
|
||||
|
||||
var _generic_call = function(function_name, args) {
|
||||
this.function_name = Extra.is.string(function_name) ? Cpp(Extra.replace.call(function_name)) : function_name;
|
||||
this.callargs = args || [];
|
||||
|
||||
this.toString = function() {
|
||||
var fname = this.function_name;
|
||||
|
||||
if (Extra.is.string(fname)) {
|
||||
fname = Global().printer.theme.callname(fname);
|
||||
}
|
||||
|
||||
return [fname, parenthesize(this.callargs.join(', '))].join(' ');
|
||||
};
|
||||
};
|
||||
|
||||
var _generic_method_call = function(object_this, function_name, args, method_sep, prefix, postfix) {
|
||||
this.object_this = Extra.is.string(object_this) ? Cpp(object_this) : object_this;
|
||||
this.function_name = Extra.is.string(function_name) ? Cpp(function_name) : function_name;
|
||||
this.callargs = args || [];
|
||||
this.method_sep = method_sep;
|
||||
this.prefix = prefix || "";
|
||||
this.postfix = postfix || "";
|
||||
|
||||
this.toString = function() {
|
||||
var fname = this.function_name;
|
||||
|
||||
if (Extra.is.string(fname)) {
|
||||
fname = Global().printer.theme.callname(fname);
|
||||
}
|
||||
|
||||
return this.prefix + this.object_this + this.method_sep + [fname, parenthesize(this.callargs.join(', '))].join(' ') + this.postfix;
|
||||
};
|
||||
};
|
||||
|
||||
var _generic_rotate = function(destination, source_a, rotation, bits, is_left) {
|
||||
this.call = 'rotate_' + (is_left ? 'left' : 'right') + bits;
|
||||
this.destination = destination;
|
||||
this.source_a = source_a;
|
||||
this.rotation = Extra.is.number(rotation) ? ("0x" + rotation.toString(16)) : rotation;
|
||||
|
||||
this.toString = function() {
|
||||
var args = [autoString(this.source_a), autoString(this.rotation)];
|
||||
|
||||
return [autoString(this.destination), '=',
|
||||
Global().printer.theme.callname(this.call),
|
||||
parenthesize(args.join(', '))
|
||||
].join(' ');
|
||||
};
|
||||
};
|
||||
|
||||
var _generic_return = function(value) {
|
||||
this.value = value;
|
||||
|
||||
this.toString = function(options) {
|
||||
var value = '';
|
||||
|
||||
if (this.value) {
|
||||
value = ' ' + autoString(this.value);
|
||||
}
|
||||
|
||||
return Global().printer.theme.flow('return') + value;
|
||||
};
|
||||
};
|
||||
|
||||
var _generic_goto = function(label_or_address) {
|
||||
this.value = label_or_address;
|
||||
|
||||
this.toString = function(options) {
|
||||
return [Global().printer.theme.flow('goto'), autoString(this.value)].join(' ');
|
||||
};
|
||||
};
|
||||
|
||||
var _generic_flow = function(name) {
|
||||
this.name = name;
|
||||
|
||||
this.toString = function(options) {
|
||||
return Global().printer.theme.flow(this.name);
|
||||
};
|
||||
};
|
||||
|
||||
var _assign_object_field = function(destination, object, field, method_sep, is_from) {
|
||||
this.destination = destination;
|
||||
this.object = object;
|
||||
this.field = field;
|
||||
this.method_sep = method_sep;
|
||||
this.is_from = is_from;
|
||||
|
||||
this.toString = function(options) {
|
||||
if (this.is_from) {
|
||||
return [
|
||||
this.object + this.method_sep + this.field,
|
||||
'=',
|
||||
this.destination
|
||||
].join(' ');
|
||||
}
|
||||
return [
|
||||
this.destination,
|
||||
'=',
|
||||
this.object + this.method_sep + this.field
|
||||
].join(' ');
|
||||
};
|
||||
};
|
||||
|
||||
var _assign_array_at = function(destination, source, index, is_from) {
|
||||
this.destination = destination;
|
||||
this.source = source;
|
||||
this.index = index;
|
||||
this.is_from = is_from;
|
||||
|
||||
this.toString = function(options) {
|
||||
if (this.is_from) {
|
||||
return [
|
||||
this.destination,
|
||||
'=',
|
||||
this.source,
|
||||
'[' + this.index + ']'
|
||||
].join(' ');
|
||||
}
|
||||
return [
|
||||
this.destination,
|
||||
'[' + this.index + ']',
|
||||
'=',
|
||||
this.source
|
||||
].join(' ');
|
||||
};
|
||||
};
|
||||
|
||||
var _generic_throw = function(value) {
|
||||
this.value = value;
|
||||
|
||||
this.toString = function(options) {
|
||||
var value = '';
|
||||
|
||||
if (this.value) {
|
||||
value = ' ' + autoString(this.value);
|
||||
}
|
||||
|
||||
return Global().printer.theme.flow('throw') + value;
|
||||
};
|
||||
};
|
||||
|
||||
var _generic_objc_call = function(object_this, function_name, function_arguments) {
|
||||
this.object_this = object_this;
|
||||
this.function_name = function_name;
|
||||
this.callargs = function_arguments || [];
|
||||
|
||||
this.toString = function() {
|
||||
var fname = this.object_this;
|
||||
if (Extra.is.string(object_this)) {
|
||||
fname = Global().printer.theme.callname(object_this);
|
||||
}
|
||||
|
||||
return "[" + [fname, this.function_name].concat(this.callargs).join(' ') + "]";
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
/* COMMON */
|
||||
assign: _assign,
|
||||
cast: function(destination, source, type) {
|
||||
return _assign(destination, new _cast(source, type));
|
||||
},
|
||||
nop: function(asm) {
|
||||
return null;
|
||||
},
|
||||
/* JUMPS */
|
||||
goto: function(label_or_address) {
|
||||
return new _generic_goto(label_or_address);
|
||||
},
|
||||
call: function(function_name, function_arguments) {
|
||||
return new _generic_call(function_name, function_arguments);
|
||||
},
|
||||
return: function(value) {
|
||||
return new _generic_return(value);
|
||||
},
|
||||
break: function(value) {
|
||||
return new _generic_flow('break');
|
||||
},
|
||||
continue: function(value) {
|
||||
return new _generic_flow('continue');
|
||||
},
|
||||
/* BRANCHES */
|
||||
conditional_assign: function(destination, source_a, source_b, cond, src_true, src_false) {
|
||||
var condition = new Condition.convert(source_a, source_b, cond, false);
|
||||
|
||||
return _assign(destination, new _texpr('?', ':', condition.toString(), src_true, src_false));
|
||||
},
|
||||
conditional_math: function(destination, source_a, source_b, cond, math_operand_a, math_operand_b, src_false, operation) {
|
||||
var condition = new Condition.convert(source_a, source_b, cond, false);
|
||||
var src_true = new _bexpr(operation, math_operand_a, math_operand_b);
|
||||
|
||||
return _assign(destination, new _texpr('?', ':', condition.toString(), src_true, src_false));
|
||||
},
|
||||
/* MATH */
|
||||
increase: function(destination, source) {
|
||||
if (source == '1') {
|
||||
return new _uexpr_pf('++', destination);
|
||||
}
|
||||
|
||||
return _assign(destination, new _bexpr('+', destination, source));
|
||||
},
|
||||
decrease: function(destination, source) {
|
||||
if (source == '1') {
|
||||
return new _uexpr_pf('--', destination);
|
||||
}
|
||||
|
||||
return _assign(destination, new _bexpr('-', destination, source));
|
||||
},
|
||||
add: function(destination, source_a, source_b) {
|
||||
if ((destination == source_a) && (source_b == '1')) {
|
||||
return new _uexpr_pf('++', destination);
|
||||
}
|
||||
|
||||
return _assign(destination, new _bexpr('+', source_a, source_b));
|
||||
},
|
||||
and: function(destination, source_a, source_b) {
|
||||
if (source_b == '0') {
|
||||
return _assign(destination, '0');
|
||||
}
|
||||
|
||||
return _assign(destination, new _bexpr('&', source_a, source_b));
|
||||
},
|
||||
divide: function(destination, source_a, source_b) {
|
||||
return _assign(destination, new _bexpr('/', source_a, source_b));
|
||||
},
|
||||
module: function(destination, source_a, source_b) {
|
||||
return _assign(destination, new _bexpr('%', source_a, source_b));
|
||||
},
|
||||
multiply: function(destination, source_a, source_b) {
|
||||
return _assign(destination, new _bexpr('*', source_a, source_b));
|
||||
},
|
||||
negate: function(destination, source) {
|
||||
return _assign(destination, new _uexpr('-', source));
|
||||
},
|
||||
not: function(destination, source) {
|
||||
return _assign(destination, new _uexpr('~', source));
|
||||
},
|
||||
extend: function(destination, source, bits) {
|
||||
return _assign(destination, new _uexpr('(int' + bits + '_t) ', source));
|
||||
},
|
||||
subtract: function(destination, source_a, source_b) {
|
||||
if ((destination == source_a) && (source_b == '0')) {
|
||||
return null;
|
||||
} else if (destination == source_a && source_b == '1') {
|
||||
return new _uexpr_pf('--', destination);
|
||||
}
|
||||
|
||||
return _assign(destination, new _bexpr('-', source_a, source_b));
|
||||
},
|
||||
or: function(destination, source_a, source_b) {
|
||||
if (source_b == '0') {
|
||||
return _assign(destination, source_a);
|
||||
}
|
||||
|
||||
return _assign(destination, new _bexpr('|', source_a, source_b));
|
||||
},
|
||||
xor: function(destination, source_a, source_b) {
|
||||
if (source_a == source_b) {
|
||||
return _assign(destination, '0');
|
||||
}
|
||||
|
||||
return _assign(destination, new _bexpr('^', source_a, source_b));
|
||||
},
|
||||
shift_left: function(destination, source_a, source_b) {
|
||||
return _assign(destination, new _bexpr('<<', source_a, source_b));
|
||||
},
|
||||
shift_right: function(destination, source_a, source_b) {
|
||||
return _assign(destination, new _bexpr('>>', source_a, source_b));
|
||||
},
|
||||
rotate_left: function(destination, source_a, source_b, bits) {
|
||||
Global().context.addDependency(new CCalls.rotate_left.fcn(bits));
|
||||
|
||||
return new _generic_rotate(destination, source_a, source_b, bits, true);
|
||||
},
|
||||
rotate_right: function(destination, source_a, source_b, bits) {
|
||||
Global().context.addDependency(new CCalls.rotate_right.fcn(bits));
|
||||
|
||||
return new _generic_rotate(destination, source_a, source_b, bits, false);
|
||||
},
|
||||
swap_endian: function(value, returns, bits) {
|
||||
Global().context.addDependency(new CCalls.swap_endian.fcn(bits));
|
||||
|
||||
return _assign(returns, new _generic_call('SWAP' + bits, [value]));
|
||||
},
|
||||
swap: function(source_a, source_b, bits) {
|
||||
var t = Variable.uniqueName('tmp');
|
||||
var ops = [
|
||||
_assign(Extra.to.type(bits, false) + ' ' + t, source_a),
|
||||
_assign(source_a, source_b),
|
||||
_assign(source_b, t),
|
||||
];
|
||||
return new function(composed) {
|
||||
this.composed = composed;
|
||||
}(ops);
|
||||
},
|
||||
bit_mask: function(destination, source_a, source_b) {
|
||||
Global().context.addDependency(new CCalls.bit_mask.fcn());
|
||||
|
||||
return _assign(destination, new _generic_call('BIT_MASK', [source_a, source_b]));
|
||||
},
|
||||
/* MEMORY */
|
||||
read_memory: function(pointer, register, bits, is_signed) {
|
||||
var value = (Extra.is.string(register) || Extra.is.number(register)) ? Variable.local(register.toString(), Extra.to.type(bits, is_signed)) : register;
|
||||
var ptr = (Extra.is.number(pointer) || Long.isLong(pointer)) ? Variable.pointer('0x' + pointer.toString(16), Extra.to.type(bits, is_signed)) : pointer;
|
||||
if (Extra.is.string(ptr)) {
|
||||
ptr = Variable.pointer(pointer, Extra.to.type(bits, is_signed));
|
||||
}
|
||||
return _assign(value, ptr);
|
||||
},
|
||||
write_memory: function(pointer, register, bits, is_signed) {
|
||||
var value = (Extra.is.string(register) || Extra.is.number(register)) ? Variable.local(register.toString(), Extra.to.type(bits, is_signed)) : register;
|
||||
var ptr = (Extra.is.number(pointer) || Long.isLong(pointer)) ? Variable.pointer('0x' + pointer.toString(16), Extra.to.type(bits, is_signed)) : pointer;
|
||||
if (Extra.is.string(ptr)) {
|
||||
ptr = Variable.pointer(pointer, Extra.to.type(bits, is_signed));
|
||||
}
|
||||
return _assign(ptr, value);
|
||||
},
|
||||
/* SPECIAL */
|
||||
composed: function(instructions) {
|
||||
return new function(composed) {
|
||||
this.composed = composed;
|
||||
}(instructions);
|
||||
},
|
||||
macro: function(macro, macro_rule) {
|
||||
Global().context.addMacro(macro_rule);
|
||||
|
||||
return new function(macro) {
|
||||
this.macro = macro;
|
||||
|
||||
this.toString = function() {
|
||||
return Global().printer.theme.macro(this.macro);
|
||||
};
|
||||
}(macro);
|
||||
},
|
||||
special: function(data) {
|
||||
return new function(data) {
|
||||
this.data = data;
|
||||
|
||||
this.toString = function() {
|
||||
return Global().printer.auto(this.data);
|
||||
};
|
||||
}(data);
|
||||
},
|
||||
/* Object based langs */
|
||||
method_call: function(object_this, method_separator, function_name, function_arguments, prefix, postfix) {
|
||||
return new _generic_method_call(object_this, function_name, function_arguments, method_separator, prefix, postfix);
|
||||
},
|
||||
objc_call: function(object_this, function_name, function_arguments) {
|
||||
return new _generic_objc_call(object_this, function_name, function_arguments);
|
||||
},
|
||||
/* Object based langs */
|
||||
assign_to_object_field: function(destination, object, method_separator, field) {
|
||||
return new _assign_object_field(destination, object, field, method_separator, false);
|
||||
},
|
||||
assign_from_object_field: function(destination, object, method_separator, field) {
|
||||
return new _assign_object_field(destination, object, field, method_separator, true);
|
||||
},
|
||||
assign_to_array: function(destination, source, index) {
|
||||
return new _assign_array_at(destination, source, index, false);
|
||||
},
|
||||
assign_from_array: function(destination, source, index) {
|
||||
return new _assign_array_at(destination, source, index, true);
|
||||
},
|
||||
throw: function(value) {
|
||||
return new _generic_throw(value);
|
||||
},
|
||||
/* UNKNOWN */
|
||||
unknown: function(asm) {
|
||||
return new _generic_asm(asm);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Long from '../long.js';
|
||||
|
||||
var Bounds = function(low, hi) {
|
||||
this.low = low;
|
||||
this.hi = hi;
|
||||
this.gt = function(bound) {
|
||||
return this.low.gt(bound.low);
|
||||
};
|
||||
this.isInside = function(addr) {
|
||||
return addr ? (addr.gte(this.low) && addr.lte(this.hi)) : false;
|
||||
};
|
||||
this.isOutside = function(addr) {
|
||||
return !this.isInside(addr);
|
||||
};
|
||||
};
|
||||
|
||||
Bounds.invalid = function() {
|
||||
return new Bounds(Long.MAX_U64_VALUE, Long.MAX_U64_VALUE);
|
||||
};
|
||||
|
||||
var _sort_extra = function(a, b) {
|
||||
if (a.address.eq(b.address)) {
|
||||
return 0;
|
||||
}
|
||||
return a.address.gt(b.address) ? 1 : -1;
|
||||
};
|
||||
|
||||
var _fill_splitted_extra = function(oldblock, newblock, name) {
|
||||
var e = oldblock[name][oldblock[name].length - 1];
|
||||
while (e && e.address.gte(newblock.bounds.low)) {
|
||||
newblock[name].push(oldblock[name].pop());
|
||||
e = oldblock[name][oldblock[name].length - 1];
|
||||
}
|
||||
};
|
||||
|
||||
export default function _block(bounds) {
|
||||
this.bounds = bounds || Bounds.invalid();
|
||||
this.extraHead = [];
|
||||
this.extraTail = [];
|
||||
this.instructions = [];
|
||||
this.addInstruction = function(instruction) {
|
||||
this.instructions.push(instruction);
|
||||
this.update();
|
||||
};
|
||||
this.addExtraHead = function(extra) {
|
||||
this.extraHead.push(extra);
|
||||
this.extraHead.sort(_sort_extra);
|
||||
};
|
||||
this.lastHead = function(extra) {
|
||||
return this.extraHead.length > 0 ? this.extraHead[this.extraHead.length - 1] : null;
|
||||
};
|
||||
this.firstTail = function(extra) {
|
||||
return this.extraTail.length > 0 ? this.extraTail[0] : null;
|
||||
};
|
||||
this.addExtraTail = function(extra) {
|
||||
this.extraTail.unshift(extra);
|
||||
this.extraTail.sort(_sort_extra);
|
||||
};
|
||||
this.update = function() {
|
||||
var l = this.instructions.length;
|
||||
if (l > 0) {
|
||||
var first = this.instructions[0];
|
||||
var last = this.instructions[l - 1];
|
||||
this.bounds = new Bounds(first.location, last.location);
|
||||
} else {
|
||||
// invalidate this block if is empty..
|
||||
this.bounds = Bounds.invalid();
|
||||
}
|
||||
};
|
||||
this.split = function(from) {
|
||||
if (from < 0 || from >= this.instructions.length) {
|
||||
return null;
|
||||
}
|
||||
var i = this.instructions.splice(from, this.instructions.length);
|
||||
var b = new _block(new Bounds(i[0].location, i[i.length - 1].location));
|
||||
b.instructions = i;
|
||||
|
||||
_fill_splitted_extra(this, b, 'extraHead');
|
||||
_fill_splitted_extra(this, b, 'extraTail');
|
||||
|
||||
this.update();
|
||||
b.update();
|
||||
return b;
|
||||
};
|
||||
this.ascomment = function() {
|
||||
for (var i = 0; i < this.instructions.length; i++) {
|
||||
this.instructions[i].ascomment();
|
||||
}
|
||||
};
|
||||
this.ascodeline = function() {
|
||||
for (var i = 0; i < this.instructions.length; i++) {
|
||||
this.instructions[i].ascodeline();
|
||||
}
|
||||
};
|
||||
this.hasPrintables = function() {
|
||||
for (var i = 0; i < this.instructions.length; i++) {
|
||||
if (this.instructions[i].valid && this.instructions[i].code) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
this.print = function() {
|
||||
var h = 0;
|
||||
var t = 0;
|
||||
var i;
|
||||
for (i = 0; i < this.instructions.length; i++) {
|
||||
while (this.extraHead[h] && this.extraHead[h].address.lte(this.instructions[i].location)) {
|
||||
this.extraHead[h].print();
|
||||
h++;
|
||||
}
|
||||
this.instructions[i].print();
|
||||
while (this.extraTail[t] && this.extraTail[t].address.eq(this.instructions[i].location)) {
|
||||
this.extraTail[t].print();
|
||||
t++;
|
||||
}
|
||||
}
|
||||
//this one is bad, but it still require to be executed.
|
||||
for (i = h; i < this.extraHead.length; i++) {
|
||||
this.extraHead[i].print();
|
||||
}
|
||||
//this one is ok.
|
||||
for (i = t; i < this.extraTail.length; i++) {
|
||||
this.extraTail[i].print();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: 2019-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
/*
|
||||
* Expects the icj json as input.
|
||||
*/
|
||||
export default function(icj) {
|
||||
var data = {};
|
||||
icj.forEach(function(x) {
|
||||
data[x.addr.toString()] = x.classname;
|
||||
});
|
||||
this.data = data;
|
||||
this.search = function(address) {
|
||||
if (address) {
|
||||
return this.data[address.toString()];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Extra from './extra.js';
|
||||
const _cmps = {
|
||||
CUST: ['', ''],
|
||||
INF: ['1', '0'],
|
||||
EQ: [' == ', ' != '],
|
||||
NE: [' != ', ' == '],
|
||||
LT: [' < ', ' >= '],
|
||||
LE: [' <= ', ' > '],
|
||||
GT: [' > ', ' <= '],
|
||||
GE: [' >= ', ' < '],
|
||||
LO: [' overflow ', ' !overflow '],
|
||||
NO: [' !overflow ', ' overflow '],
|
||||
INSTANCEOF: [' instanceof ', null],
|
||||
};
|
||||
|
||||
export default {
|
||||
inf: function() {
|
||||
this.toString = function() {
|
||||
return Global().printer.theme.integers('1');
|
||||
};
|
||||
},
|
||||
convert: function(a, b, cond, invert) {
|
||||
this.invert = invert;
|
||||
this.condition = cond; // ? _cmps[cond][invert ? 1 : 0] : '';
|
||||
this.a = a;
|
||||
this.b = b || '';
|
||||
/* main method */
|
||||
this.toString = function() {
|
||||
var a = Extra.is.string(this.a) ? Global().printer.auto(this.a) : this.a;
|
||||
var b = Extra.is.string(this.b) ? Global().printer.auto(this.b) : this.b;
|
||||
if (this.invert && _cmps[this.condition][1]) {
|
||||
return a + _cmps[this.condition][1] + b;
|
||||
} else if (this.invert) {
|
||||
return '!(' + a + Global().printer.theme.flow(_cmps[this.condition][0]) + b + ')';
|
||||
}
|
||||
return a + _cmps[this.condition][0] + b;
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,333 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Base from './base.js';
|
||||
import Utils from './utils.js';
|
||||
import Scope from './scope.js';
|
||||
import Variable from './variable.js';
|
||||
import Condition from './condition.js';
|
||||
|
||||
var _compare_locations = function(a, b) {
|
||||
if (a.eq(b.location)) {
|
||||
return 0;
|
||||
}
|
||||
return a.lt(b.location) ? 1 : -1;
|
||||
};
|
||||
|
||||
var _compare_blocks = function(a, b) {
|
||||
if (a == b) {
|
||||
return 0;
|
||||
}
|
||||
return a.bounds.gt(b.bounds) ? 1 : -1;
|
||||
};
|
||||
|
||||
var _condition = function(instruction, invert) {
|
||||
return instruction.cond ? new Condition.convert(instruction.cond.a, instruction.cond.b, instruction.cond.type, invert) : new Condition.inf();
|
||||
};
|
||||
|
||||
var ControlFlowContext = function(blocks, instructions) {
|
||||
this.instructions = instructions;
|
||||
this.labels = [];
|
||||
this.addLabel = function(label) {
|
||||
if (label) {
|
||||
this.labels.push(label);
|
||||
}
|
||||
};
|
||||
this.findLabel = function(location) {
|
||||
for (var i = 0; i < this.labels.length; i++) {
|
||||
if (this.labels[i].address.eq(location)) {
|
||||
return this.labels[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
this.blocks = blocks;
|
||||
this.addBlock = function(blk) {
|
||||
if (blk) {
|
||||
this.blocks.push(blk);
|
||||
this.blocks = this.blocks.sort(_compare_blocks);
|
||||
}
|
||||
};
|
||||
this.findBlock = function(location) {
|
||||
for (var i = 0; i < this.blocks.length; i++) {
|
||||
if (this.blocks[i].bounds.isInside(location)) {
|
||||
return this.blocks[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var _set_goto = function(instruction, label) {
|
||||
if (!instruction.code || !instruction.valid) {
|
||||
instruction.code = Base.goto(label);
|
||||
instruction.valid = true;
|
||||
} else if (instruction.code.composed) {
|
||||
instruction.code.composed.push(Base.goto(label));
|
||||
} else {
|
||||
instruction.code = Base.composed([instruction.code, Base.goto(label)]);
|
||||
}
|
||||
};
|
||||
|
||||
var _set_outbounds_jump = function(instruction, index, context) {
|
||||
if (Utils.search(instruction.jump, context.instructions, _compare_locations) && context.findBlock(instruction.location)) {
|
||||
return false;
|
||||
}
|
||||
if (!instruction.code) {
|
||||
//instruction.code = Base.goto('0x' + instruction.jump.toString());
|
||||
var call = Variable.functionPointer('0x' + instruction.jump.toString(16), 0, []);
|
||||
instruction.code = Base.call(call, []);
|
||||
// if we have a jump as the last instruction, then we will have a return call for sure.
|
||||
}
|
||||
|
||||
if (context.instructions.indexOf(instruction) == (context.instructions.length - 1)) {
|
||||
instruction.code = Base.return(instruction.code);
|
||||
if (instruction.cond) {
|
||||
instruction.comments.push('Beware that this jump is a conditional jump.');
|
||||
instruction.comments.push('jsdec transformed it as a return, due being the');
|
||||
instruction.comments.push('last instruction. Please, check \'pdda\' output');
|
||||
instruction.comments.push('for more hints.');
|
||||
}
|
||||
} else if (instruction.cond) {
|
||||
var block = context.findBlock(instruction.location);
|
||||
if (block) {
|
||||
var single_instr = block.split(block.instructions.indexOf(instruction));
|
||||
if (single_instr) {
|
||||
single_instr.addExtraHead(new Scope.if(instruction.location, _condition(instruction, false)));
|
||||
single_instr.addExtraTail(new Scope.brace(instruction.location));
|
||||
context.addBlock(single_instr);
|
||||
context.addBlock(single_instr.split(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instruction.setBadJump();
|
||||
return true;
|
||||
};
|
||||
|
||||
var _set_loops = function(instruction, index, context) {
|
||||
// loops jumps only backwards or to the same location.
|
||||
var single_instr = null;
|
||||
if (instruction.jump.gt(instruction.location)) {
|
||||
return false;
|
||||
}
|
||||
var block = context.findBlock(instruction.location);
|
||||
if (!block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// let's check if is a oneline loop or panic loop
|
||||
if (instruction.jump.eq(instruction.location)) {
|
||||
single_instr = block.split(index);
|
||||
if (single_instr) {
|
||||
if (!instruction.code) {
|
||||
single_instr.addExtraHead(new Scope.whileInline(instruction.jump, _condition(instruction)));
|
||||
} else {
|
||||
single_instr.addExtraHead(new Scope.do(instruction.jump));
|
||||
single_instr.addExtraTail(new Scope.whileEnd(instruction.location, _condition(instruction)));
|
||||
}
|
||||
context.addBlock(single_instr);
|
||||
context.addBlock(single_instr.split(1));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var loop_start = Utils.search(instruction.jump, block.instructions, _compare_locations);
|
||||
if (!loop_start) {
|
||||
var label = context.findLabel(instruction.jump);
|
||||
if (!label) {
|
||||
label = Variable.newLabel(instruction.jump);
|
||||
context.addLabel(label);
|
||||
}
|
||||
_set_goto(instruction, label);
|
||||
if (instruction.cond) {
|
||||
single_instr = block.split(block.instructions.indexOf(instruction));
|
||||
// here the jump is taken only if the condition is true.
|
||||
if (single_instr) {
|
||||
single_instr.addExtraHead(new Scope.if(instruction.location, _condition(instruction, false)));
|
||||
single_instr.addExtraTail(new Scope.brace(instruction.location));
|
||||
context.addBlock(single_instr);
|
||||
context.addBlock(single_instr.split(1));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
var loop_start_index = block.instructions.indexOf(loop_start);
|
||||
var previous = block.instructions[loop_start_index - 1];
|
||||
var loop_block = block.split(loop_start_index);
|
||||
|
||||
var next = loop_block.split(loop_block.instructions.indexOf(instruction) + 1);
|
||||
|
||||
// let's check it there is a jump inside the loop
|
||||
// if it is, then it's a while (cond) {}
|
||||
if (previous && previous.jump && previous.jump.gte(instruction.jump) && previous.jump.lte(instruction.location)) {
|
||||
previous.setBadJump();
|
||||
loop_block.addExtraHead(new Scope.while(instruction.jump, _condition(instruction)));
|
||||
loop_block.addExtraTail(new Scope.brace(instruction.location));
|
||||
} else {
|
||||
// ok it's a do {} while (cond);
|
||||
loop_block.addExtraHead(new Scope.do(instruction.jump));
|
||||
loop_block.addExtraTail(new Scope.whileEnd(instruction.location, _condition(instruction)));
|
||||
}
|
||||
|
||||
context.addBlock(loop_block);
|
||||
context.addBlock(next);
|
||||
return true;
|
||||
};
|
||||
|
||||
var _set_if_else = function(instruction, index, context) {
|
||||
var label = null;
|
||||
// if/else jumps only forward and inside the block.
|
||||
if (instruction.jump.lte(instruction.location)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if jumps to the next instruction, just ignore it..
|
||||
if (!instruction.cond && context.instructions[index + 1] && context.instructions[index + 1].location.eq(instruction.jump)) {
|
||||
instruction.setBadJump();
|
||||
return false;
|
||||
}
|
||||
if (!instruction.cond) {
|
||||
if (!instruction.code) {
|
||||
label = context.findLabel(instruction.jump);
|
||||
if (!label) {
|
||||
label = Variable.newLabel(instruction.jump);
|
||||
context.addLabel(label);
|
||||
}
|
||||
instruction.code = Base.goto(label);
|
||||
} else if (instruction.code.composed) {
|
||||
label = context.findLabel(instruction.jump);
|
||||
if (!label) {
|
||||
label = Variable.newLabel(instruction.jump);
|
||||
context.addLabel(label);
|
||||
}
|
||||
instruction.code.composed.push(Base.goto(label));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var block = context.findBlock(instruction.location);
|
||||
if (!block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var outside = Utils.search(instruction.jump, block.instructions, _compare_locations);
|
||||
|
||||
// not found. let's use goto.
|
||||
if (!outside) {
|
||||
label = context.findLabel(instruction.jump);
|
||||
if (!label) {
|
||||
label = Variable.newLabel(instruction.jump);
|
||||
context.addLabel(label);
|
||||
}
|
||||
_set_goto(instruction, label);
|
||||
if (instruction.cond) {
|
||||
var single_instr = block.split(block.instructions.indexOf(instruction));
|
||||
if (!single_instr) {
|
||||
return true;
|
||||
}
|
||||
// here the jump is taken only if the condition is true.
|
||||
single_instr.addExtraHead(new Scope.if(instruction.location, _condition(instruction, false)));
|
||||
single_instr.addExtraTail(new Scope.brace(instruction.location));
|
||||
context.addBlock(single_instr);
|
||||
context.addBlock(single_instr.split(1));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// let's create the if block.
|
||||
var if_block = block.split(block.instructions.indexOf(instruction));
|
||||
if (!if_block) {
|
||||
return false;
|
||||
}
|
||||
if_block.addExtraHead(new Scope.if(instruction.location, _condition(instruction, true)));
|
||||
|
||||
|
||||
// let's get the last element inside the if (jump instr -1).
|
||||
var outside_index = if_block.instructions.indexOf(outside);
|
||||
var last_if_instruction = if_block.instructions[outside_index - 1];
|
||||
if (!last_if_instruction) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// let's check if the last instruction is a jump forward (outside), that can lead to an else
|
||||
if (last_if_instruction.jump && !last_if_instruction.cond && last_if_instruction.jump.gt(last_if_instruction.location)) {
|
||||
// ok we have an else. let's search for the last instruction.
|
||||
var first_else_instruction = outside;
|
||||
var instr_after_else = Utils.search(last_if_instruction.jump, context.instructions, _compare_locations);
|
||||
var last_else_instruction = context.instructions[context.instructions.indexOf(instr_after_else) - 1];
|
||||
if (Utils.search(last_else_instruction.location, if_block.instructions, _compare_locations) && first_else_instruction != instr_after_else) {
|
||||
var lh = if_block.lastHead();
|
||||
var ft = if_block.firstTail();
|
||||
if (lh && lh.isElse && ft && ft.address.eq(last_else_instruction.location)) {
|
||||
if_block.addExtraTail(new Scope.brace(first_else_instruction.location));
|
||||
if_block.addExtraHead(new Scope.else(first_else_instruction.location));
|
||||
} else {
|
||||
if_block.addExtraHead(new Scope.else(first_else_instruction.location));
|
||||
if_block.addExtraTail(new Scope.brace(last_else_instruction.location));
|
||||
}
|
||||
last_if_instruction.setBadJump();
|
||||
outside_index = if_block.instructions.indexOf(last_else_instruction) + 1;
|
||||
} else {
|
||||
if_block.addExtraTail(new Scope.brace(last_if_instruction.location));
|
||||
}
|
||||
} else {
|
||||
if_block.addExtraTail(new Scope.brace(last_if_instruction.location));
|
||||
}
|
||||
|
||||
context.addBlock(if_block);
|
||||
context.addBlock(if_block.split(outside_index));
|
||||
return true;
|
||||
};
|
||||
|
||||
var _set_custom_flow = function(instruction, index, context) {
|
||||
var block = context.findBlock(instruction.location);
|
||||
if (!block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var first_external_instruction = Utils.search(instruction.jump, block.instructions, _compare_locations);
|
||||
var if_block = block.split(block.instructions.indexOf(instruction));
|
||||
if (!if_block) {
|
||||
return false;
|
||||
}
|
||||
var first_external_instruction_index = if_block.instructions.indexOf(first_external_instruction);
|
||||
if_block.addExtraHead(new Scope.custom(instruction.location, instruction.customflow));
|
||||
if_block.addExtraTail(new Scope.brace(first_external_instruction.location));
|
||||
context.addBlock(if_block);
|
||||
context.addBlock(if_block.split(first_external_instruction_index));
|
||||
return true;
|
||||
};
|
||||
|
||||
export default function(session) {
|
||||
var j, i;
|
||||
var context = new ControlFlowContext(session.blocks, session.instructions);
|
||||
|
||||
for (j = 0; j < session.instructions.length; j++) {
|
||||
if (!session.instructions[j].jump || !session.instructions[j].customflow) {
|
||||
continue;
|
||||
}
|
||||
_set_custom_flow(session.instructions[j], j, context);
|
||||
}
|
||||
|
||||
for (j = 0; j < session.instructions.length; j++) {
|
||||
if (!session.instructions[j].jump || session.instructions[j].customflow) {
|
||||
continue;
|
||||
}
|
||||
if (!_set_outbounds_jump(session.instructions[j], j, context)) {
|
||||
_set_loops(session.instructions[j], j, context);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < session.instructions.length; j++) {
|
||||
if (!session.instructions[j].jump || session.instructions[j].customflow) {
|
||||
continue;
|
||||
}
|
||||
_set_if_else(session.instructions[j], j, context);
|
||||
}
|
||||
|
||||
for (i = 0; i < context.labels.length; i++) {
|
||||
var instruction = Utils.search(context.labels[i].address, session.instructions, _compare_locations);
|
||||
instruction.label = context.labels[i];
|
||||
}
|
||||
|
||||
session.blocks = context.blocks.sort(_compare_blocks);
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import _call_common from '../db/macros.js';
|
||||
import Long from '../long.js';
|
||||
|
||||
/**
|
||||
* Types size
|
||||
* @type {Object}
|
||||
*/
|
||||
const _standard_types = {
|
||||
'void': 0,
|
||||
'char': 8,
|
||||
'short': 16,
|
||||
'long': 32,
|
||||
'float': 32,
|
||||
'long long': 64,
|
||||
'double': 64,
|
||||
};
|
||||
|
||||
const _magic_math_values = {
|
||||
"0x069c16bd": 'if there is a right shift of 0, then it\'s a division by 665/25756',
|
||||
"0x10624dd3": 'if there is a right shift of 4, then it\'s a division by 1/250',
|
||||
"0x2aaaaaab": 'if there is a right shift of 0, then it\'s a division by 1/6',
|
||||
"0x30c30c31": 'if there is a right shift of 3, then it\'s a division by 1/42',
|
||||
"0x38e38e39": 'if there is a right shift of 1, then it\'s a division by 1/9',
|
||||
"0x4ec4ec4f": 'if there is a right shift of 3, then it\'s a division by 1/26',
|
||||
"0x51eb851f": 'if there is a right shift of 5, then it\'s a division by 1/100',
|
||||
"0x55555556": 'if there is a right shift of 0, then it\'s a division by 1/3',
|
||||
"0x66666667": 'if there is a right shift of 1, then it\'s a division by 1/5',
|
||||
"0x6bca1af3": 'if there is a right shift of 5, then it\'s a division by 1/76',
|
||||
"0x88888889": 'if there is a right shift of 8, then it\'s a division by 1/480',
|
||||
"0x92492493": 'if there is a right shift of 3, then it\'s a division by 1/14',
|
||||
"0xa0a0a0a1": 'if there is a right shift of 7, then it\'s a division by 1/204',
|
||||
"0xaaaaaaab": 'if there is a right shift of 1, then it\'s a division by 1/3',
|
||||
"0xae147ae1": 'if there is a right shift of 5, then it\'s a division by 17/800',
|
||||
"0xb21642c9": 'if there is a right shift of 5, then it\'s a division by 1/46',
|
||||
"0xb60b60b7": 'if there is a right shift of 5, then it\'s a division by 1/45',
|
||||
"0xba2e8ba3": 'if there is a right shift of 3, then it\'s a division by 1/11',
|
||||
"0xea0ea0eb": 'if there is a right shift of 0, then it\'s a division by 32/35',
|
||||
};
|
||||
|
||||
var _align_address = function(x) {
|
||||
var zeros32 = '00000000';
|
||||
var c = x.toString(16);
|
||||
if (c.length > zeros32.length) {
|
||||
return '0x' + c;
|
||||
}
|
||||
return '0x' + zeros32.substr(c.length, zeros32.length) + c;
|
||||
};
|
||||
|
||||
/**
|
||||
* Is helpers
|
||||
* @type {Object}
|
||||
*/
|
||||
const _is = {
|
||||
string: function(s) {
|
||||
return typeof s == 'string';
|
||||
},
|
||||
number: function(s) {
|
||||
return typeof s == 'number';
|
||||
},
|
||||
array: function(s) {
|
||||
return Array.isArray(s);
|
||||
},
|
||||
inObject: function(object, value) {
|
||||
for (var k in object) {
|
||||
if (object[k] == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* To helpers
|
||||
* @type {Object}
|
||||
*/
|
||||
const _to = {
|
||||
type: function(bits, signed) {
|
||||
if (bits == 0 || !bits) {
|
||||
return 'void';
|
||||
}
|
||||
return (signed ? 'int' : 'uint') + bits + '_t';
|
||||
},
|
||||
bits: function(type) {
|
||||
var bits = Global().evars.archbits;
|
||||
type = type.replace(/[un]?signed\s?/, '');
|
||||
if (type.length == 0) {
|
||||
type = bits < 32 ? 'int16_t' : 'int32_t';
|
||||
}
|
||||
if (_standard_types[type]) {
|
||||
return _standard_types[type];
|
||||
}
|
||||
if (type == 'int') {
|
||||
return bits < 32 ? 16 : 32;
|
||||
}
|
||||
return parseInt(type.replace(/[intu_]/g, ''));
|
||||
},
|
||||
array: function(object) {
|
||||
var a = [];
|
||||
for (var key in object) {
|
||||
a.push(object[key]);
|
||||
}
|
||||
return a;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Find helpers
|
||||
* @type {Object}
|
||||
*/
|
||||
const _find = {
|
||||
arguments_number: function(name) {
|
||||
name = _replace.call(name);
|
||||
if (_call_common[name]) {
|
||||
return _call_common[name].required;
|
||||
} else if (Global().argdb) {
|
||||
var db = Global().argdb;
|
||||
for (var k in db) {
|
||||
if (_replace.call(db[k].name.replace(/^_+/, '')) == name) {
|
||||
if (db[k].count) {
|
||||
return parseInt(db[k].count.toString());
|
||||
}
|
||||
return db[k].args.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
call_additional: function(name) {
|
||||
name = _replace.call(name);
|
||||
if (_call_common[name]) {
|
||||
return !!_call_common[name].varargs;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace helpers
|
||||
* @type {Object}
|
||||
*/
|
||||
const _replace = {
|
||||
call: function(name) {
|
||||
if (typeof name != 'string' || name.startsWith('0x')) {
|
||||
return name;
|
||||
}
|
||||
if (name.startsWith('sym.imp.')) {
|
||||
name = name.substring('sym.imp.'.length).replace(/^_+/, '');
|
||||
} else if (name.startsWith('sym.')) {
|
||||
name = name.substring('sym.'.length).replace(/^_+/, '');
|
||||
} else if (name.startsWith('imp.')) {
|
||||
name = name.substring('imp.'.length).replace(/^_+/, '');
|
||||
//} else if (name.startsWith('fcn.')) {
|
||||
// name = name.substring('fcn.'.length);
|
||||
//} else if (name.startsWith('func.')) {
|
||||
// name = name.substring('func.'.length);
|
||||
}
|
||||
if (name.startsWith('reloc.')) {
|
||||
name = name.substring('reloc.'.length).replace(/^_+/, '');
|
||||
}
|
||||
name = name.replace(/^sub\./, '');
|
||||
if (name.match(/^[\w]+\.dll_/)) {
|
||||
name = name.replace(/[\w]+\.dll_/, '');
|
||||
}
|
||||
name = name.replace(/[Mm][Ss][Vv][Cc][Rr][Tt]\.[Dd][Ll][Ll]_/, '');
|
||||
name = name.replace(/\./g, '_');
|
||||
return name.replace(/(\w|^):(\w|$)/g, '$1_$2').replace(/_+/g, '_');
|
||||
},
|
||||
object: function(name) {
|
||||
if (typeof name != 'string' || name.startsWith('0x')) {
|
||||
return name;
|
||||
}
|
||||
if (name.startsWith('sym.imp.')) {
|
||||
name = name.substring('sym.imp.'.length);
|
||||
} else if (name.startsWith('sym.')) {
|
||||
name = name.substring('sym.'.length);
|
||||
} else if (name.startsWith('imp.')) {
|
||||
name = name.substring('imp.'.length);
|
||||
//} else if (name.startsWith('fcn.')) {
|
||||
// name = name.substring('fcn.'.length);
|
||||
//} else if (name.startsWith('func.')) {
|
||||
// name = name.substring('func.'.length);
|
||||
} else if (name.startsWith('reloc.')) {
|
||||
name = name.substring('reloc.'.length);
|
||||
}
|
||||
if (name.startsWith('L') || name.startsWith('[L')) {
|
||||
// java object
|
||||
name = name.replace(/\.<init>\(.+$|\(.+$|\._init_.+V$|_L.+$/, '');
|
||||
name = name.substr(name.startsWith('L') ? 1 : 2).replace(/\/|;->/g, '.').replace(/\s.+$|;$/, '');
|
||||
if (name.indexOf('_L') > 0) {
|
||||
name = name.substring(0, name.indexOf('_L'));
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
const _tryas = {
|
||||
int: function(x) {
|
||||
try {
|
||||
return Long.from(x.trim(), false, (x.trim().indexOf('0x') == 0 ? 16 : 10));
|
||||
} catch (e) {}
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extra object
|
||||
* @return {Function} - Extra object
|
||||
*/
|
||||
export default {
|
||||
tryas: _tryas,
|
||||
is: _is,
|
||||
to: _to,
|
||||
find: _find,
|
||||
replace: _replace,
|
||||
align_address: _align_address,
|
||||
magic_math: function(address) {
|
||||
if (typeof address != 'string') {
|
||||
address = '0x' + address.toString(16);
|
||||
}
|
||||
return _magic_math_values[address];
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import rzpipe from '../rzpipe.js';
|
||||
import Utils from './utils.js';
|
||||
|
||||
var _compare_search = function(a, b) {
|
||||
if (a.eq(b.offset)) {
|
||||
return 0;
|
||||
} else if (a.lt(b.offset)) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
var _compare_offsets = function(a, b) {
|
||||
return a.offset.lt(b.offset) ? -1 : (a.offset.eq(b.offset) ? 0 : 1);
|
||||
};
|
||||
|
||||
var _compare_name = function(a, b) {
|
||||
return a.name < b.name ? -1 : (a.name == b.name ? 0 : 1);
|
||||
};
|
||||
|
||||
var create_fcn_data = function(x) {
|
||||
if (!x) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
offset: x.offset,
|
||||
name: x.name,
|
||||
calltype: x.calltype,
|
||||
nargs: x.nargs
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Expects the aflj json as input.
|
||||
*/
|
||||
export default function(aflj, sort_by_name) {
|
||||
this.data = aflj.sort(sort_by_name ? _compare_name : _compare_offsets).map(function(x) {
|
||||
return create_fcn_data(x);
|
||||
});
|
||||
|
||||
this.search = function(offset) {
|
||||
if (!Global().evars.extra.slow && offset) {
|
||||
var x = rzpipe.json('afij @ 0x' + offset.toString(16), [])[0];
|
||||
return create_fcn_data(x);
|
||||
}
|
||||
return offset ? Utils.search(offset, this.data, _compare_search) : null;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Condition from './condition.js';
|
||||
import Extra from './extra.js';
|
||||
import Long from '../long.js';
|
||||
|
||||
var _printable = function(instr) {
|
||||
return instr.valid && instr.code && instr.code.toString().length > 0;
|
||||
};
|
||||
|
||||
var _conditional = function(cond) {
|
||||
return (new Condition.convert(cond.a, cond.b, cond.type, false)).toString();
|
||||
};
|
||||
|
||||
var _asm_rizin_view = function(instr, ascodeline) {
|
||||
var s = '';
|
||||
if (instr.label) {
|
||||
s = instr.label.name + ":\n";
|
||||
}
|
||||
if (instr.code && instr.code.composed) {
|
||||
if (instr.cond && instr.jump) {
|
||||
s += (instr.jump.gt(instr.location) ? "if" : "while") + " (" + _conditional(instr.cond) + ") {\n";
|
||||
}
|
||||
for (var i = 0; i < instr.code.composed.length; i++) {
|
||||
s += instr.code.composed[i] + '\n';
|
||||
}
|
||||
if (instr.cond && instr.jump) {
|
||||
s += "}\n";
|
||||
}
|
||||
} else if (_printable(instr)) {
|
||||
if (instr.cond && instr.jump) {
|
||||
s += "if (" + _conditional(instr.cond) + ") " + instr.code + "\n";
|
||||
} else {
|
||||
s += instr.code + "\n";
|
||||
}
|
||||
} else if (instr.cond && !_printable(instr) && instr.jump) {
|
||||
s += "if (" + _conditional(instr.cond) + ") goto 0x" + instr.jump.toString(16);
|
||||
}
|
||||
|
||||
if (ascodeline) {
|
||||
Global().evars.add_code_line(s.trim(), instr.location);
|
||||
} else {
|
||||
Global().evars.add_comment(s.trim(), instr.location);
|
||||
}
|
||||
};
|
||||
|
||||
var _asm_view = function(instr) {
|
||||
var i, t, b, s, addr;
|
||||
if (Global().evars.honor.blocks) {
|
||||
return;
|
||||
}
|
||||
if (Global().evars.honor.offsets) {
|
||||
t = Global().printer.theme;
|
||||
addr = Extra.align_address(instr.location);
|
||||
if (instr.code && instr.code.composed) {
|
||||
Global().context.printLine(Global().context.identfy(addr.length, t.integers(addr)) + instr.code.composed[0] + ';', instr.location);
|
||||
for (i = 1; i < instr.code.composed.length; i++) {
|
||||
Global().context.printLine(Global().context.identfy(addr.length, t.integers(addr)) + instr.code.composed[i] + ';', instr.location);
|
||||
}
|
||||
} else if (_printable(instr)) {
|
||||
Global().context.printLine(Global().context.identfy(addr.length, t.integers(addr)) + instr.code + ';', instr.location);
|
||||
}
|
||||
} else if (Global().evars.honor.assembly) {
|
||||
t = Global().printer.theme;
|
||||
b = Global().printer.auto;
|
||||
addr = Extra.align_address(instr.location);
|
||||
s = 1 + addr.length + instr.simplified.length;
|
||||
if (instr.code && instr.code.composed) {
|
||||
Global().context.printLine(Global().context.identfy(s, t.integers(addr) + ' ' + b(instr.simplified)) + instr.code.composed[0] + ';', instr.location);
|
||||
for (i = 1; i < instr.code.composed.length; i++) {
|
||||
Global().context.printLine(Global().context.identfy() + instr.code.composed[i] + ';', instr.location);
|
||||
}
|
||||
} else {
|
||||
Global().context.printLine(Global().context.identfy(s, t.integers(addr) + ' ' + b(instr.simplified)) + (_printable(instr) ? (instr.code + ';') : ''), instr.location);
|
||||
}
|
||||
} else {
|
||||
if (instr.code && instr.code.composed) {
|
||||
for (i = 0; i < instr.code.composed.length; i++) {
|
||||
Global().context.printLine(Global().context.identfy() + instr.code.composed[i] + ';', instr.location);
|
||||
}
|
||||
} else if (_printable(instr)) {
|
||||
Global().context.printLine(Global().context.identfy() + instr.code + ';', instr.location);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var _instruction = function(data, arch, marker) {
|
||||
this.code = null;
|
||||
this.marker = marker;
|
||||
this.valid = true;
|
||||
this.jump = data.jump ? Long.from(data.jump, true) : null;
|
||||
this.type = data.type;
|
||||
this.pointer = (data.ptr && Long.ZERO.lt(data.ptr)) ? data.ptr : null;
|
||||
this.location = Long.from(data.offset, true);
|
||||
this.assembly = data.disasm || data.opcode;
|
||||
this.simplified = data.opcode;
|
||||
this.cpp_type = this.assembly.match(/(class|method|struct)\s[\w:]+(<[\w:<, >]+>)?\s+/);
|
||||
if (this.cpp_type) {
|
||||
this.assembly = this.assembly.replace(this.cpp_type[0], '');
|
||||
}
|
||||
this.parsed = arch.parse(this.assembly, this.simplified);
|
||||
this.string = null;
|
||||
this.symbol = null;
|
||||
this.klass = null;
|
||||
this.callee = null;
|
||||
this.label = null;
|
||||
this.cond = null;
|
||||
this.customflow = null;
|
||||
this.xrefs = data.xrefs ? data.xrefs.slice() : [];
|
||||
this.refs = data.refs ? data.refs.slice() : [];
|
||||
this.comments = data.comment ? [data.comment] : [];
|
||||
if (Global().evars.honor.xrefs) {
|
||||
for (var i = 0; i < this.xrefs.length; i++) {
|
||||
var e = 'XREF ' + this.xrefs[i].type + ": 0x" + this.xrefs[i].addr.toString(16);
|
||||
this.comments.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.conditional = function(a, b, type) {
|
||||
if (type) {
|
||||
this.cond = {
|
||||
a: a,
|
||||
b: b,
|
||||
type: type
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
this.setBadJump = function() {
|
||||
this.jump = null;
|
||||
};
|
||||
|
||||
this.print = function() {
|
||||
var t = Global().printer.theme;
|
||||
var empty = Global().context.identfy();
|
||||
if (this.comments.length == 1) {
|
||||
Global().context.printLine(empty + t.comment('/* ' + this.comments[0] + ' */'), this.location);
|
||||
} else if (this.comments.length > 1) {
|
||||
Global().context.printLine(empty + t.comment('/* ' + this.comments[0]), this.location);
|
||||
for (var i = 1; i < this.comments.length; i++) {
|
||||
var comment = ' * ' + this.comments[i] + (i == this.comments.length - 1 ? ' */' : '');
|
||||
Global().context.printLine(empty + t.comment(comment), this.location);
|
||||
}
|
||||
}
|
||||
if (this.label) {
|
||||
Global().context.printLine(Global().context.identfy(null, null, true) + this.label + ':', this.location);
|
||||
}
|
||||
_asm_view(this);
|
||||
};
|
||||
this.ascomment = function() {
|
||||
_asm_rizin_view(this, false);
|
||||
};
|
||||
this.ascodeline = function() {
|
||||
_asm_rizin_view(this, true);
|
||||
};
|
||||
};
|
||||
|
||||
_instruction.swap = function(instructions, index_a, index_b) {
|
||||
var a = instructions[index_a];
|
||||
var b = instructions[index_b];
|
||||
instructions[index_b] = a;
|
||||
instructions[index_a] = b;
|
||||
|
||||
var a_loc = a.location;
|
||||
|
||||
a.location = b.location;
|
||||
b.location = a_loc;
|
||||
|
||||
|
||||
if (a.jump && b.location.eq(a.jump)) {
|
||||
a.jump = a.location;
|
||||
} else if (b.jump && a.location.eq(b.jump)) {
|
||||
b.jump = b.location;
|
||||
}
|
||||
};
|
||||
|
||||
export default _instruction;
|
||||
@@ -0,0 +1,12 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
export default function(data) {
|
||||
this.data = data;
|
||||
this.print = function() {
|
||||
var t = Global().printer.theme;
|
||||
for (var i = 0; i < this.data.length; i++) {
|
||||
Global().context.printLine(Global().context.identfy() + t.macro(this.data[i]));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
// SPDX-FileCopyrightText: 2019-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Extra from './extra.js';
|
||||
import Base from './base.js';
|
||||
|
||||
const _x86_stret = {
|
||||
'32': {
|
||||
returns: 'rdi',
|
||||
receiver: 'eax',
|
||||
selector: 'ecx',
|
||||
args: ['edx', 'ecx', 'r8d', 'r9d']
|
||||
},
|
||||
'64': {
|
||||
returns: 'rdi',
|
||||
receiver: 'rsi',
|
||||
selector: 'rdx',
|
||||
args: ['rcx', 'r8', 'r9', 'r10']
|
||||
}
|
||||
};
|
||||
|
||||
const _x86 = {
|
||||
'32': {
|
||||
receiver: 'eax',
|
||||
selector: 'ecx',
|
||||
returns: 'eax',
|
||||
args: ['edx', 'ecx', 'r8d', 'r9d']
|
||||
},
|
||||
'64': {
|
||||
receiver: 'rdi',
|
||||
selector: 'rsi',
|
||||
returns: 'rax',
|
||||
args: [
|
||||
['edx', 'rdx'],
|
||||
['ecx', 'rcx'],
|
||||
['r8d', 'r8'],
|
||||
['r9d', 'r9']
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const _ppc = {
|
||||
receiver: 'r3',
|
||||
selector: 'r4',
|
||||
returns: 'r3',
|
||||
args: ['r5', 'r6', 'r7', 'r8']
|
||||
};
|
||||
|
||||
const _ppc_stret = {
|
||||
returns: 'r3',
|
||||
receiver: 'r4',
|
||||
selector: 'r5',
|
||||
args: ['r6', 'r7', 'r8', 'r9']
|
||||
};
|
||||
|
||||
const _arm = {
|
||||
'16': {
|
||||
receiver: 'r0',
|
||||
selector: 'r1',
|
||||
returns: 'r0',
|
||||
args: ['r2', 'r3', 'r4', 'r5']
|
||||
},
|
||||
'32': {
|
||||
receiver: 'r0',
|
||||
selector: 'r1',
|
||||
returns: 'r0',
|
||||
args: ['r2', 'r3', 'r4', 'r5']
|
||||
},
|
||||
'64': {
|
||||
receiver: 'x0',
|
||||
selector: 'x1',
|
||||
returns: 'x0',
|
||||
args: ['x2', 'x3', 'x4', 'x5']
|
||||
}
|
||||
};
|
||||
|
||||
const _arm_stret = {
|
||||
'16': {
|
||||
receiver: 'r1',
|
||||
selector: 'r2',
|
||||
returns: 'r0',
|
||||
args: ['r3', 'r4', 'r5', 'r6']
|
||||
},
|
||||
'32': {
|
||||
receiver: 'r1',
|
||||
selector: 'r2',
|
||||
returns: 'r0',
|
||||
args: ['r3', 'r4', 'r5', 'r6']
|
||||
}
|
||||
};
|
||||
|
||||
const _object_c_registers = {
|
||||
'x86': {
|
||||
'32': ['rdi', 'rsi', 'rdx', 'rcx', 'r8', 'r9'],
|
||||
'64': ['edi', 'esi', 'edx', 'ecx', 'r8d', 'r9d'],
|
||||
},
|
||||
'ppc': {
|
||||
'32': ['r3', 'r4', 'r5', 'r6', 'r7', 'r8'],
|
||||
'64': ['r3', 'r4', 'r5', 'r6', 'r7', 'r8'],
|
||||
},
|
||||
'arm': {
|
||||
'16': ['r0', 'r1', 'r2', 'r3', 'r4', 'r5'],
|
||||
'32': ['r0', 'r1', 'r2', 'r3', 'r4', 'r5'],
|
||||
'64': ['x0', 'x1', 'x2', 'x3', 'x4', 'x5'],
|
||||
}
|
||||
};
|
||||
|
||||
const _object_c_class_methods = {
|
||||
objc_msgSend: {
|
||||
'x86': _x86,
|
||||
'ppc': {
|
||||
'32': _ppc,
|
||||
'64': _ppc
|
||||
},
|
||||
'arm': _arm
|
||||
},
|
||||
objc_msgSend_fpret: {
|
||||
'x86': _x86,
|
||||
'ppc': {
|
||||
'32': _ppc,
|
||||
'64': _ppc
|
||||
},
|
||||
'arm': _arm
|
||||
},
|
||||
objc_msgSend_noarg: {
|
||||
'x86': _x86,
|
||||
'ppc': {
|
||||
'32': _ppc,
|
||||
'64': _ppc
|
||||
},
|
||||
'arm': _arm
|
||||
},
|
||||
objc_msgSendSuper: {
|
||||
'x86': _x86,
|
||||
'ppc': {
|
||||
'32': _ppc,
|
||||
'64': _ppc
|
||||
},
|
||||
'arm': _arm
|
||||
},
|
||||
objc_msgSendSuper2: {
|
||||
'x86': _x86,
|
||||
'ppc': {
|
||||
'32': _ppc,
|
||||
'64': _ppc
|
||||
},
|
||||
'arm': _arm
|
||||
},
|
||||
objc_msgSend_stret: {
|
||||
'x86': _x86_stret,
|
||||
'ppc': {
|
||||
'32': _ppc_stret,
|
||||
'64': _ppc_stret
|
||||
},
|
||||
'arm': _arm_stret
|
||||
},
|
||||
objc_msgSendSuper_stret: {
|
||||
'x86': _x86_stret,
|
||||
'ppc': {
|
||||
'32': _ppc_stret,
|
||||
'64': _ppc_stret
|
||||
},
|
||||
'arm': _arm_stret
|
||||
},
|
||||
objc_msgSendSuper2_stret: {
|
||||
'x86': _x86_stret,
|
||||
'ppc': {
|
||||
'32': _ppc_stret,
|
||||
'64': _ppc_stret
|
||||
},
|
||||
'arm': _arm_stret
|
||||
},
|
||||
/* fixup */
|
||||
objc_msgSend_fixup: {
|
||||
'x86': _x86,
|
||||
'ppc': {
|
||||
'32': _ppc,
|
||||
'64': _ppc
|
||||
},
|
||||
'arm': _arm
|
||||
},
|
||||
objc_msgSend_fpret_fixup: {
|
||||
'x86': _x86,
|
||||
'ppc': {
|
||||
'32': _ppc,
|
||||
'64': _ppc
|
||||
},
|
||||
'arm': _arm
|
||||
},
|
||||
objc_msgSendSuper2_fixup: {
|
||||
'x86': _x86,
|
||||
'ppc': {
|
||||
'32': _ppc,
|
||||
'64': _ppc
|
||||
},
|
||||
'arm': _arm
|
||||
},
|
||||
objc_msgSend_stret_fixup: {
|
||||
'x86': _x86_stret,
|
||||
'ppc': {
|
||||
'32': _ppc_stret,
|
||||
'64': _ppc_stret
|
||||
},
|
||||
'arm': _arm_stret
|
||||
},
|
||||
objc_msgSendSuper2_stret_fixup: {
|
||||
'x86': _x86_stret,
|
||||
'ppc': {
|
||||
'32': _ppc_stret,
|
||||
'64': _ppc_stret
|
||||
},
|
||||
'arm': _arm_stret
|
||||
}
|
||||
};
|
||||
|
||||
const _object_c_block_methods = {
|
||||
objc_autoreleasePoolPush: function(instr, context, instructions) {
|
||||
if (!context.object_c.autorelease) {
|
||||
context.object_c.autorelease = [instr];
|
||||
} else {
|
||||
context.object_c.autorelease.push(instr);
|
||||
}
|
||||
instr.customflow = '@' + Global().printer.theme.flow('autoreleasepool');
|
||||
return Base.nop();
|
||||
},
|
||||
objc_autoreleasePoolPop: function(instr, context, instructions) {
|
||||
context.object_c.autorelease.pop().jump = instr.location;
|
||||
return Base.nop();
|
||||
},
|
||||
objc_retainAutoreleasedReturnValue: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
objc_release: function() {
|
||||
var arch = Global().evars.arch;
|
||||
var bits = Global().evars.archbits.toString();
|
||||
return Base.objc_call(_object_c_registers[arch][bits][0], 'release');
|
||||
},
|
||||
objc_retain: function() {
|
||||
var arch = Global().evars.arch;
|
||||
var bits = Global().evars.archbits.toString();
|
||||
return Base.objc_call(_object_c_registers[arch][bits][0], 'retain');
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
arguments: function(callname) {
|
||||
callname = Extra.replace.call(callname);
|
||||
var arch = Global().evars.arch;
|
||||
var bits = Global().evars.archbits.toString();
|
||||
return _object_c_class_methods[callname][arch][bits].args.slice();
|
||||
},
|
||||
returns: function(callname) {
|
||||
callname = Extra.replace.call(callname);
|
||||
var arch = Global().evars.arch;
|
||||
var bits = Global().evars.archbits.toString();
|
||||
return _object_c_class_methods[callname][arch][bits].returns;
|
||||
},
|
||||
receiver: function(callname) {
|
||||
callname = Extra.replace.call(callname);
|
||||
var arch = Global().evars.arch;
|
||||
var bits = Global().evars.archbits.toString();
|
||||
return _object_c_class_methods[callname][arch][bits].receiver;
|
||||
},
|
||||
selector: function(callname) {
|
||||
callname = Extra.replace.call(callname);
|
||||
var arch = Global().evars.arch;
|
||||
var bits = Global().evars.archbits.toString();
|
||||
return _object_c_class_methods[callname][arch][bits].selector;
|
||||
},
|
||||
handle_others: function(callname, instr, context, instructions) {
|
||||
if (_object_c_block_methods[callname]) {
|
||||
if (!context.object_c) {
|
||||
context.object_c = {};
|
||||
}
|
||||
return _object_c_block_methods[callname](instr, context, instructions);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
is_class_method: function(callname) {
|
||||
callname = Extra.replace.call(callname);
|
||||
return !!_object_c_class_methods[callname];
|
||||
},
|
||||
is: function(callname) {
|
||||
callname = Extra.replace.call(callname);
|
||||
return callname.startsWith('objc_') && (
|
||||
_object_c_block_methods[callname] ||
|
||||
_object_c_class_methods[callname]
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: 2019-2021 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Extra from './extra.js';
|
||||
|
||||
const _java = {
|
||||
array: function(type, size, create, init) {
|
||||
this.size = size || 0;
|
||||
this.type = Extra.replace.object(type);
|
||||
this.init = init;
|
||||
this.toString = function() {
|
||||
var t = [Global().printer.theme.flow('new'), Global().printer.theme.callname(this.type), '[' + this.size + ']'];
|
||||
if (this.init && this.init.length > 0) {
|
||||
t.push('{' + this.init.join(', ') + '}');
|
||||
}
|
||||
return t.join(' ');
|
||||
};
|
||||
},
|
||||
object: function(type, args, create) {
|
||||
this.args = args || [];
|
||||
this.type = Extra.replace.object(type);
|
||||
this.create = create || false;
|
||||
this.toString = function() {
|
||||
var a = this.args.length > 0 ? '(' + this.args.join(', ') + ')' : '';
|
||||
if (this.create) {
|
||||
return [Global().printer.theme.flow('new'), Global().printer.theme.callname(this.type), a].join(' ').trim();
|
||||
}
|
||||
return [Global().printer.theme.callname(this.type), a].join(' ').trim();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const _all_langs = {
|
||||
java: _java,
|
||||
dalvik: _java,
|
||||
};
|
||||
|
||||
export default {
|
||||
array: function(type, size, create, lang, init) {
|
||||
lang = lang || Global().evars.arch;
|
||||
if (!_all_langs[lang]) {
|
||||
throw new Error('Missing lang for array (objects.js)');
|
||||
}
|
||||
return new _all_langs[lang].array(type, size, create, init || []);
|
||||
},
|
||||
object: function(type, args, create, lang) {
|
||||
lang = lang || Global().evars.arch;
|
||||
if (!_all_langs[lang]) {
|
||||
throw new Error('Missing lang for object (objects.js)');
|
||||
}
|
||||
return new _all_langs[lang].object(type, args, create);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,167 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Extra from './extra.js';
|
||||
var __debug = false;
|
||||
|
||||
function _print_locals(locals, address, spaced) {
|
||||
if (Global().evars.honor.blocks) {
|
||||
return;
|
||||
}
|
||||
var a = Global().printer.auto;
|
||||
for (var i = 0; i < locals.length; i++) {
|
||||
var local = Extra.is.string(locals[i]) ? a(locals[i]) : locals[i].toString(true);
|
||||
Global().context.printLine(Global().context.identfy() + local + ';', address);
|
||||
}
|
||||
if (spaced && locals.length > 0) {
|
||||
Global().context.printLine(Global().context.identfy(), address);
|
||||
}
|
||||
}
|
||||
|
||||
function _print_block_data(block) {
|
||||
if (Global().evars.honor.blocks) {
|
||||
var t = Global().printer.theme;
|
||||
var ident = Global().context.identfy();
|
||||
var addr = block.address.toString(16);
|
||||
Global().context.printLine(ident + t.comment('/* address 0x' + addr + ' */'), block.address);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
brace: function(address) {
|
||||
this.address = address;
|
||||
this.toString = function() {
|
||||
return '}' + (__debug ? Global().printer.theme.comment(' // 0x' + this.address.toString(16)) : '');
|
||||
};
|
||||
this.print = function() {
|
||||
Global().context.identOut();
|
||||
var offset = Global().evars.honor.offsets ? Extra.align_address(this.address) : '';
|
||||
Global().context.printLine(Global().context.identfy(offset.length, Global().printer.theme.integers(offset)) + this.toString(), this.address);
|
||||
};
|
||||
},
|
||||
custom: function(address, colorname) {
|
||||
this.address = address;
|
||||
this.colorname = colorname;
|
||||
this.toString = function() {
|
||||
return this.colorname + ' {' + (__debug ? Global().printer.theme.comment(' // 0x' + this.address.toString(16)) : '');
|
||||
};
|
||||
this.print = function() {
|
||||
var offset = Global().evars.honor.offsets ? Extra.align_address(this.address) : '';
|
||||
Global().context.printLine(Global().context.identfy(offset.length, Global().printer.theme.integers(offset)) + this.toString(), this.address);
|
||||
Global().context.identIn();
|
||||
_print_block_data(this);
|
||||
};
|
||||
},
|
||||
routine: function(address, extra) {
|
||||
this.address = address;
|
||||
this.extra = extra;
|
||||
this.toString = function() {
|
||||
var e = this.extra;
|
||||
var t = Global().printer.theme;
|
||||
var a = Global().printer.auto;
|
||||
return t.types(e.returns) + ' ' + t.callname(e.routine_name) + ' (' + e.args.map(function(x) {
|
||||
return Extra.is.string(x) ? a(x) : x.toString(true);
|
||||
}).join(', ') + ') {' + (__debug ? t.comment(' // 0x' + this.address.toString(16)) : '');
|
||||
};
|
||||
this.print = function() {
|
||||
var e = this.extra;
|
||||
var t = Global().printer.theme;
|
||||
_print_locals(e.globals, this.address, true);
|
||||
var asmname = Global().evars.honor.offsets ? Extra.align_address(this.address) : '; (fcn) ' + e.name + ' ()';
|
||||
var color = Global().evars.honor.offsets ? 'integers' : 'comment';
|
||||
var ident = Global().context.identfy(asmname.length, t[color](asmname));
|
||||
Global().context.printLine(ident + this.toString(), this.address);
|
||||
Global().context.identIn();
|
||||
_print_block_data(this);
|
||||
_print_locals(e.locals, this.address);
|
||||
};
|
||||
},
|
||||
if: function(address, condition, locals) {
|
||||
this.address = address;
|
||||
this.condition = condition;
|
||||
this.locals = locals || [];
|
||||
this.toString = function() {
|
||||
var t = Global().printer.theme;
|
||||
return t.flow('if') + ' (' + this.condition + ') {' + (__debug ? t.comment(' // 0x' + this.address.toString(16)) : '');
|
||||
};
|
||||
this.print = function() {
|
||||
var offset = Global().evars.honor.offsets ? Extra.align_address(this.address) : '';
|
||||
Global().context.printLine(Global().context.identfy(offset.length, Global().printer.theme.integers(offset)) + this.toString(), this.address);
|
||||
Global().context.identIn();
|
||||
_print_block_data(this);
|
||||
_print_locals(this.locals, this.address);
|
||||
};
|
||||
},
|
||||
else: function(address, locals) {
|
||||
this.isElse = true;
|
||||
this.address = address;
|
||||
this.locals = locals || [];
|
||||
this.toString = function() {
|
||||
var t = Global().printer.theme;
|
||||
return '} ' + t.flow('else') + ' {' + (__debug ? t.comment(' // 0x' + this.address.toString(16)) : '');
|
||||
};
|
||||
this.print = function() {
|
||||
Global().context.identOut();
|
||||
var offset = Global().evars.honor.offsets ? Extra.align_address(this.address) : '';
|
||||
Global().context.printLine(Global().context.identfy(offset.length, Global().printer.theme.integers(offset)) + this.toString(), this.address);
|
||||
Global().context.identIn();
|
||||
_print_block_data(this);
|
||||
_print_locals(this.locals, this.address);
|
||||
};
|
||||
},
|
||||
do: function(address, locals) {
|
||||
this.address = address;
|
||||
this.locals = locals || [];
|
||||
this.toString = function() {
|
||||
var t = Global().printer.theme;
|
||||
return t.flow('do') + ' {' + (__debug ? t.comment(' // 0x' + this.address.toString(16)) : '');
|
||||
};
|
||||
this.print = function() {
|
||||
Global().context.printLine(Global().context.identfy() + this.toString(), this.address);
|
||||
Global().context.identIn();
|
||||
_print_block_data(this);
|
||||
_print_locals(this.locals, this.address);
|
||||
};
|
||||
},
|
||||
while: function(address, condition, locals) {
|
||||
this.address = address;
|
||||
this.condition = condition;
|
||||
this.locals = locals || [];
|
||||
this.toString = function() {
|
||||
var t = Global().printer.theme;
|
||||
return t.flow('while') + ' (' + this.condition + ') {' + (__debug ? t.comment(' // 0x' + this.address.toString(16)) : '');
|
||||
};
|
||||
this.print = function() {
|
||||
var offset = Global().evars.honor.offsets ? Extra.align_address(this.address) : '';
|
||||
Global().context.printLine(Global().context.identfy(offset.length, Global().printer.theme.integers(offset)) + this.toString(), this.address);
|
||||
Global().context.identIn();
|
||||
_print_block_data(this);
|
||||
_print_locals(this.locals, this.address);
|
||||
};
|
||||
},
|
||||
whileEnd: function(address, condition) {
|
||||
this.address = address;
|
||||
this.condition = condition;
|
||||
this.toString = function() {
|
||||
var t = Global().printer.theme;
|
||||
return '} ' + t.flow('while') + ' (' + this.condition + ');' + (__debug ? t.comment(' // 0x' + this.address.toString(16)) : '');
|
||||
};
|
||||
this.print = function() {
|
||||
Global().context.identOut();
|
||||
Global().context.printLine(Global().context.identfy() + this.toString(), this.address);
|
||||
};
|
||||
},
|
||||
whileInline: function(address, condition) {
|
||||
this.address = address;
|
||||
this.condition = condition;
|
||||
this.toString = function() {
|
||||
var t = Global().printer.theme;
|
||||
return t.flow('while') + ' (' + this.condition + ');' + (__debug ? t.comment(' // 0x' + this.address.toString(16)) : '');
|
||||
};
|
||||
this.print = function() {
|
||||
_print_block_data(this);
|
||||
var offset = Global().evars.honor.offsets ? Extra.align_address(this.address) : '';
|
||||
Global().context.printLine(Global().context.identfy(offset.length, Global().printer.theme.integers(offset)) + this.toString(), this.address);
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import rzpipe from '../rzpipe.js';
|
||||
import Utils from './utils.js';
|
||||
|
||||
var _compare = function(a, b) {
|
||||
if (a.eq(b.location)) {
|
||||
return 0;
|
||||
} else if (a.lt(b.location)) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
var _str_compare_location = function(a, b) {
|
||||
return a.location.lt(b.location) ? -1 : (a.location.eq(b.location) ? 0 : 1);
|
||||
};
|
||||
|
||||
var _str_compare_size = function(a, b) {
|
||||
return a.value < b.value ? -1 : (a.value == b.value ? 0 : 1);
|
||||
};
|
||||
|
||||
var _sanitize = function(x) {
|
||||
return x.paddr || x.vaddr || x.offset;
|
||||
};
|
||||
|
||||
var _flag_filter = function(x) {
|
||||
var ch = x.charAt(0);
|
||||
if ((ch >= 'a'.charAt(0) && ch <= 'z'.charAt(0)) || (ch >= 'A'.charAt(0) && ch <= 'Z'.charAt(0)) || (ch >= '0'.charAt(0) && ch <= '9'.charAt(0))) {
|
||||
return x;
|
||||
}
|
||||
switch (ch) {
|
||||
case '\\'.charAt(0):
|
||||
case ':'.charAt(0):
|
||||
case '.'.charAt(0):
|
||||
case '_'.charAt(0):
|
||||
return x;
|
||||
}
|
||||
return '_';
|
||||
};
|
||||
|
||||
/*
|
||||
* Expects the Csj json as input.
|
||||
*/
|
||||
export default function(Csj, sort_by_size) {
|
||||
this.data = Csj.filter(_sanitize).map(function(x) {
|
||||
return {
|
||||
location: Global().evars.honor.paddr ? x.paddr : x.vaddr || x.offset,
|
||||
value: atob(x.string || x.name).replace(/\\\\/g, '\\')
|
||||
};
|
||||
}).sort(sort_by_size ? _str_compare_size : _str_compare_location);
|
||||
this.search = function(address) {
|
||||
if (address) {
|
||||
if (!Global().evars.extra.slow) {
|
||||
var x = rzpipe.string('Cs.q @ 0x' + address.toString(16));
|
||||
if (x) {
|
||||
x = x.substr(1);
|
||||
return x.substr(0, x.length - 1);
|
||||
}
|
||||
}
|
||||
var r = Utils.search(address, this.data, _compare);
|
||||
return r ? r.value : null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
this.search_by_flag = function(flag) {
|
||||
if (flag && flag.startsWith('str.')) {
|
||||
if (!Global().evars.extra.slow) {
|
||||
var address = rzpipe.string('s @ ' + flag);
|
||||
var x = rzpipe.string('Cs.q @ ' + address.toString(16));
|
||||
if (x) {
|
||||
x = x.substr(1);
|
||||
return x.substr(0, x.length - 1);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < this.data.length; i++) {
|
||||
var elem = 'str.' + this.data[i].value.split('').map(_flag_filter).join('').trim();
|
||||
elem = elem.replace(/\\[abnrtv]/g, '_').replace(/\\/g, '_');
|
||||
elem = elem.replace(/^str._+/, 'str.');
|
||||
elem = elem.replace(/_+$/, '');
|
||||
if (elem == flag) {
|
||||
return this.data[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import rzpipe from '../rzpipe.js';
|
||||
import Utils from './utils.js';
|
||||
import Long from '../long.js';
|
||||
|
||||
var _compare = function(a, b) {
|
||||
if (a.eq(b.location)) {
|
||||
return 0;
|
||||
} else if (a.lt(b.location)) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
var _virtual_compare = function(a, b) {
|
||||
return a.vaddr.lt(b.vaddr) ? -1 : (a.vaddr.eq(b.vaddr) ? 0 : 1);
|
||||
};
|
||||
|
||||
var _physical_compare = function(a, b) {
|
||||
return a.paddr.lt(b.paddr) ? -1 : (a.paddr.eq(b.paddr) ? 0 : 1);
|
||||
};
|
||||
|
||||
var _sanitize = function(x) {
|
||||
return x.paddr || x.vaddr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Expects the isj json as input.
|
||||
*/
|
||||
export default function(isj) {
|
||||
this.data = isj.filter(_sanitize).sort(Global().evars.honor.paddr ? _physical_compare : _virtual_compare).map(function(x) {
|
||||
return {
|
||||
location: Global().evars.honor.paddr ? x.paddr : x.vaddr,
|
||||
value: (x.demname && x.demname.length > 0) ? x.demname : x.name,
|
||||
};
|
||||
});
|
||||
this.search = function(address) {
|
||||
const evars = Global().evars;
|
||||
if (!address) {
|
||||
return null;
|
||||
} else if (!Long.isLong(address)) {
|
||||
address = Long.from(address, true);
|
||||
}
|
||||
if (!evars.extra.slow) {
|
||||
var def = {
|
||||
symbols: {}
|
||||
};
|
||||
var x = rzpipe.json('is.j @ 0x' + address.toString(16), def);
|
||||
if (x.length != 1) {
|
||||
return null;
|
||||
}
|
||||
x = x[0];
|
||||
var loc = (evars.honor.paddr ? x.paddr : x.vaddr) || Long.MAX_U64_VALUE;
|
||||
return address.eq(loc) && !Long.MAX_U64_VALUE.eq(loc) ? ((x.demname && x.demname.length > 0) ? x.demname : x.name) : null;
|
||||
}
|
||||
var r = Utils.search(address, this.data, _compare);
|
||||
return r ? r.value : null;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
var _default_cmp = function(a, b) {
|
||||
return a - b;
|
||||
};
|
||||
|
||||
export default {
|
||||
indexOf: function(value, array, compare) {
|
||||
if (!compare) {
|
||||
compare = _default_cmp;
|
||||
}
|
||||
var min = 0;
|
||||
var max = array.length - 1;
|
||||
var index;
|
||||
while (min <= max) {
|
||||
index = (min + max) >> 1;
|
||||
var cmp = compare(value, array[index]);
|
||||
if (cmp === 0) {
|
||||
return index;
|
||||
} else {
|
||||
if (cmp < 0) {
|
||||
min = index + 1;
|
||||
} else {
|
||||
max = index - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
search: function(value, array, compare) {
|
||||
var pos = this.indexOf(value, array, compare);
|
||||
return pos >= 0 ? array[pos] : null;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,184 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-FileCopyrightText: 2018-2019 elicn
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Extra from './extra.js';
|
||||
import Objects from './objects.js';
|
||||
import Long from '../long.js';
|
||||
|
||||
var _internal_label_cnt = 0;
|
||||
var _internal_variable_cnt = 0;
|
||||
|
||||
// ---------- inner functions ----------
|
||||
|
||||
var parenthesize = function(s) {
|
||||
return ['(', s, ')'].join('');
|
||||
};
|
||||
|
||||
var autoParen = function(s) {
|
||||
return (s.indexOf(' ') > (-1) ? parenthesize(s) : s);
|
||||
};
|
||||
|
||||
// ---------- inner objects ----------
|
||||
|
||||
var _label = function(name, address) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
|
||||
this.toString = function() {
|
||||
return Global().printer.theme.labels(this.name);
|
||||
};
|
||||
};
|
||||
|
||||
var _func_ptr = function(name, type, args) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.args = args.map(function(x) {
|
||||
return x.toType ? x.toType() : ('uint' + Global().evars.archbits + '_t');
|
||||
});
|
||||
|
||||
this.toType = function() {
|
||||
return Global().printer.theme.types(this.type) + ' (*)(' + this.args.join(', ') + ')';
|
||||
};
|
||||
|
||||
this.toString = function() {
|
||||
return Global().printer.theme.types(this.type) + ' (*' + Global().printer.auto(this.name) + ')(' + this.args.join(', ') + ')';
|
||||
};
|
||||
};
|
||||
|
||||
var _ptr = function(name, type, pointer) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.pointer = pointer;
|
||||
|
||||
this.toType = function() {
|
||||
return Global().printer.theme.types(this.type) + '*';
|
||||
};
|
||||
|
||||
this.toString = function(define) {
|
||||
if (define) {
|
||||
var suffix = '';
|
||||
if (this.pointer) {
|
||||
suffix = ' = ' + Global().printer.auto(this.pointer);
|
||||
}
|
||||
return Global().printer.theme.types(this.type) + '* ' + this.name + suffix;
|
||||
}
|
||||
|
||||
var c = '*(';
|
||||
if (Global().evars.honor.casts) {
|
||||
c += '(' + Global().printer.theme.types(this.type) + '*) ';
|
||||
}
|
||||
|
||||
return c + Global().printer.auto(autoParen(this.name)) + ')';
|
||||
};
|
||||
};
|
||||
|
||||
var _local = function(name, type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
|
||||
this.toType = function() {
|
||||
return Global().printer.theme.types(this.type);
|
||||
};
|
||||
|
||||
this.toString = function(define) {
|
||||
if (define) {
|
||||
return Global().printer.theme.types(this.type) + ' ' + Global().printer.auto(this.name);
|
||||
}
|
||||
|
||||
return Global().printer.auto(this.name);
|
||||
};
|
||||
};
|
||||
|
||||
var _string = function(content) {
|
||||
this.content = content;
|
||||
if (!this.content.startsWith('"') && !this.content.startsWith("'")) {
|
||||
this.content = '"' + this.content;
|
||||
}
|
||||
if (!this.content.endsWith('"') && !this.content.endsWith("'")) {
|
||||
this.content += '"';
|
||||
}
|
||||
this.content = this.content.replace(/\n/g, '\\n').replace(/\r/g, '\\r');
|
||||
|
||||
this.toType = function() {
|
||||
return Global().printer.theme.types('char') + '*';
|
||||
};
|
||||
|
||||
this.toString = function(define) {
|
||||
return define ? null : Global().printer.theme.text(this.content);
|
||||
};
|
||||
};
|
||||
|
||||
var _number = function(content) {
|
||||
this.content = Long.isLong(content) ? ('0x' + content.toString(16)) : content;
|
||||
|
||||
this.toType = function() {
|
||||
return '';
|
||||
};
|
||||
|
||||
this.toString = function(define) {
|
||||
return define ? null : Global().printer.theme.integers(this.content);
|
||||
};
|
||||
};
|
||||
|
||||
var _macro = function(content) {
|
||||
this.content = content;
|
||||
|
||||
this.toType = function() {
|
||||
return '';
|
||||
};
|
||||
|
||||
this.toString = function(define) {
|
||||
return define ? null : Global().printer.theme.macro(this.content);
|
||||
};
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
|
||||
export default {
|
||||
uniqueName: function(variable_name) {
|
||||
var n = _internal_variable_cnt++;
|
||||
|
||||
return [variable_name ? variable_name : "value", n].join('_');
|
||||
},
|
||||
newLabel: function(address) {
|
||||
var n = _internal_label_cnt++;
|
||||
|
||||
return new _label(['label', n].join('_'), address);
|
||||
},
|
||||
functionPointer: function(variable_name, bits, arguments_type) {
|
||||
return new _func_ptr(variable_name, Extra.to.type(bits || 0), arguments_type || []);
|
||||
},
|
||||
globalPointer: function(variable_name, ctype_or_bits, is_signed, pointer) {
|
||||
var ctype = Extra.is.number(ctype_or_bits) ? Extra.to.type(ctype_or_bits, is_signed) : ctype_or_bits;
|
||||
|
||||
return new _ptr(variable_name, ctype, pointer);
|
||||
},
|
||||
pointer: function(variable_name, ctype_or_bits, is_signed) {
|
||||
var ctype = Extra.is.number(ctype_or_bits) ? Extra.to.type(ctype_or_bits, is_signed) : ctype_or_bits;
|
||||
|
||||
return new _ptr(variable_name, ctype);
|
||||
},
|
||||
local: function(variable_name, ctype_or_bits, is_signed) {
|
||||
var ctype = Extra.is.number(ctype_or_bits) ? Extra.to.type(ctype_or_bits, is_signed) : ctype_or_bits;
|
||||
return new _local(variable_name, ctype);
|
||||
},
|
||||
string: function(string_content) {
|
||||
return new _string(string_content);
|
||||
},
|
||||
number: function(number) {
|
||||
return new _number(number);
|
||||
},
|
||||
macro: function(string_content) {
|
||||
return new _macro(string_content);
|
||||
},
|
||||
object: function(type, args) {
|
||||
return Objects.object(type, args, false);
|
||||
},
|
||||
newobject: function(type, args) {
|
||||
return Objects.object(type, args, true);
|
||||
},
|
||||
newarray: function(type, size, init) {
|
||||
return Objects.array(type, size, true, null, init);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: 2019-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
export default function(strings, symbols, classes) {
|
||||
this.db_strings = strings;
|
||||
this.db_symbols = symbols;
|
||||
this.db_classes = classes;
|
||||
this.find_symbol = function(address) {
|
||||
return this.db_symbols.search(address);
|
||||
};
|
||||
this.find_string = function(value) {
|
||||
if (typeof value == 'string') {
|
||||
return this.db_strings.search_by_flag(value);
|
||||
}
|
||||
return this.db_strings.search(value);
|
||||
};
|
||||
this.find_class = function(address) {
|
||||
return this.db_classes.search(address);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
var mident = ' ';
|
||||
var _unique_print = {
|
||||
rotate_left: [],
|
||||
rotate_right: [],
|
||||
bit_mask: false,
|
||||
swap_endian: [],
|
||||
};
|
||||
|
||||
export default {
|
||||
rotate_left: {
|
||||
macros: ['#include <stdint.h>', '#include <limits.h>'],
|
||||
fcn: function(bits) {
|
||||
this.bits = '' + bits;
|
||||
this.name = 'rotate_left###';
|
||||
this.returns = 'uint###_t';
|
||||
this.args = ['uint###_t', 'value', 'uint32_t', 'count'];
|
||||
this.data = [
|
||||
mident + 'const uint###_t mask = (CHAR_BIT * sizeof (value)) - 1;',
|
||||
mident + 'count &= mask;',
|
||||
mident + 'return (value << count) | (value >> (-count & mask));'
|
||||
];
|
||||
this.print = function(offset) {
|
||||
if (_unique_print.rotate_left.indexOf(this.bits) >= 0) {
|
||||
return;
|
||||
}
|
||||
_unique_print.rotate_left.push(this.bits);
|
||||
var i;
|
||||
var bits = this.bits;
|
||||
var call = this.name.replace(/###/g, bits);
|
||||
var type = this.returns.replace(/###/g, bits);
|
||||
var args = this.args.map(function(x) {
|
||||
return x.replace(/###/g, bits);
|
||||
});
|
||||
args = args[0] + ' ' + args[1] + ', ' + args[2] + ' ' + args[3];
|
||||
var a = Global().printer.auto;
|
||||
var t = Global().printer.theme;
|
||||
Global().context.printLine(Global().context.identfy() + t.types(type) + ' ' + t.callname(call) + ' (' + args + ') {', offset);
|
||||
for (i = 0; i < this.data.length; i++) {
|
||||
Global().context.printLine(Global().context.identfy() + a(this.data[i].replace(/###/g, this.bits)), offset);
|
||||
}
|
||||
Global().context.printLine(Global().context.identfy() + '}', offset);
|
||||
};
|
||||
}
|
||||
},
|
||||
rotate_right: {
|
||||
macros: ['#include <stdint.h>', '#include <limits.h>'],
|
||||
fcn: function(bits) {
|
||||
this.bits = '' + bits;
|
||||
this.name = 'rotate_right###';
|
||||
this.returns = 'uint###_t';
|
||||
this.args = ['uint###_t', 'value', 'uint32_t', 'count'];
|
||||
this.data = [
|
||||
mident + 'const uint###_t mask = (CHAR_BIT * sizeof (value)) - 1;',
|
||||
mident + 'count &= mask;',
|
||||
mident + 'return (value >> count) | (value << (-count & mask));'
|
||||
];
|
||||
this.print = function(offset) {
|
||||
if (_unique_print.rotate_right.indexOf(this.bits) >= 0) {
|
||||
return;
|
||||
}
|
||||
_unique_print.rotate_right.push(this.bits);
|
||||
var i;
|
||||
var bits = this.bits;
|
||||
var call = this.name.replace(/###/g, bits);
|
||||
var type = this.returns.replace(/###/g, bits);
|
||||
var args = this.args.map(function(x) {
|
||||
return x.replace(/###/g, bits);
|
||||
});
|
||||
var a = Global().printer.auto;
|
||||
var t = Global().printer.theme;
|
||||
args = args[0] + ' ' + args[1] + ', ' + args[2] + ' ' + args[3];
|
||||
Global().context.printLine(Global().context.identfy() + t.types(type) + ' ' + t.callname(call) + ' (' + args + ') {', offset);
|
||||
for (i = 0; i < this.data.length; i++) {
|
||||
Global().context.printLine(Global().context.identfy() + a(this.data[i].replace(/###/g, this.bits)), offset);
|
||||
}
|
||||
Global().context.printLine(Global().context.identfy() + '}', offset);
|
||||
};
|
||||
}
|
||||
},
|
||||
bit_mask: {
|
||||
macros: [
|
||||
'#include <limits.h>',
|
||||
],
|
||||
fcn: function() {
|
||||
this.mask = '#define BIT_MASK(t,v) ((t)(-((v)!= 0)))&(((t)-1)>>((sizeof(t)*CHAR_BIT)-(v)))';
|
||||
this.print = function(offset) {
|
||||
if (_unique_print.bit_mask) {
|
||||
return;
|
||||
}
|
||||
_unique_print.bit_mask = true;
|
||||
var t = Global().printer.theme;
|
||||
Global().context.printLine(Global().context.identfy() + t.macro(this.mask), offset);
|
||||
};
|
||||
}
|
||||
},
|
||||
swap_endian: {
|
||||
macros: [],
|
||||
fcn: function(bits) {
|
||||
this.bits = bits;
|
||||
this.data = [];
|
||||
if (bits == 16) {
|
||||
this.data.push('#define SWAP16(n) ((uint16_t) (((n & 0x00ff) << 8) | \\');
|
||||
this.data.push(' ((n & 0xff00) >> 8)))');
|
||||
} else if (bits == 32) {
|
||||
this.data.push('#define SWAP32(n) ((uint32_t) (((n & 0x000000ff) << 24) | \\');
|
||||
this.data.push(' ((n & 0x0000ff00) << 8) | \\');
|
||||
this.data.push(' ((n & 0x00ff0000) >> 8) | \\');
|
||||
this.data.push(' ((n & 0xff000000) >> 24)))');
|
||||
} else {
|
||||
this.bits = 64;
|
||||
this.data.push('#define SWAP64(val) ((uint64_t) (((val & 0x00000000000000ffull) << 56) | \\');
|
||||
this.data.push(' ((val & 0x000000000000ff00ull) << 40) | \\');
|
||||
this.data.push(' ((val & 0x0000000000ff0000ull) << 24) | \\');
|
||||
this.data.push(' ((val & 0x00000000ff000000ull) << 8) | \\');
|
||||
this.data.push(' ((val & 0x000000ff00000000ull) >> 8) | \\');
|
||||
this.data.push(' ((val & 0x0000ff0000000000ull) >> 24) | \\');
|
||||
this.data.push(' ((val & 0x00ff000000000000ull) >> 40) | \\');
|
||||
this.data.push(' ((val & 0xff00000000000000ull) >> 56)))');
|
||||
}
|
||||
this.print = function(offset) {
|
||||
if (_unique_print.swap_endian.indexOf(this.bits) >= 0) {
|
||||
return;
|
||||
}
|
||||
_unique_print.swap_endian.push(this.bits);
|
||||
var t = Global().printer.theme;
|
||||
for (var i = 0; i < this.data.length; i++) {
|
||||
Global().context.printLine(Global().context.identfy() + t.macro(this.data[i]), offset);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
const _known_cpp_data = {
|
||||
'std::basic_string<char,std::char_traits<char>,std::allocator<char>>,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>': 'std::string',
|
||||
'std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::basic_stringstream': 'std::stringstream ',
|
||||
'std::basic_istringstream<char,std::char_traits<char>,std::allocator<char>>::basic_istringstream': 'std::istringstream ',
|
||||
'std::basic_ostringstream<char,std::char_traits<char>,std::allocator<char>>::basic_ostringstream': 'std::ostringstream ',
|
||||
'std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::~basic_stringstream': 'std::~stringstream ',
|
||||
'std::basic_istringstream<char,std::char_traits<char>,std::allocator<char>>::~basic_istringstream': 'std::~istringstream ',
|
||||
'std::basic_ostringstream<char,std::char_traits<char>,std::allocator<char>>::~basic_ostringstream': 'std::~ostringstream ',
|
||||
'std::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string': 'std::string ',
|
||||
'std::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string': 'std::~string ',
|
||||
'std::basic_string<char,std::char_traits<char>,std::allocator<char>>': 'std::string ',
|
||||
'std::basic_ostream<char,std::char_traits<char>>': 'std::string ',
|
||||
'std::basic_istream<char,std::char_traits<char>>': 'std::string ',
|
||||
|
||||
'operatordelete(void*)': 'delete',
|
||||
|
||||
'<char,std::char_traits<char>,std::allocator<char>>': ' ',
|
||||
'<std::char_traits<char>>': 'std::string ',
|
||||
|
||||
'std::_Ios_Openmode': 'std::ios_base::openmode ',
|
||||
|
||||
'&std::operator>>': '& std::operator >> ',
|
||||
'&std::operator<<': '& std::operator << ',
|
||||
'&std::operator[]': '& std::operator [] ',
|
||||
'&std::operator|': '& std::operator | ',
|
||||
'&std::operator+': '& std::operator + ',
|
||||
'&std::operator+=': '& std::operator += ',
|
||||
|
||||
'operator>>': 'operator >> ',
|
||||
'operator<<': 'operator << ',
|
||||
'operator[]': 'operator [] ',
|
||||
'operator|': 'operator | ',
|
||||
|
||||
'std::stringconst': 'std::string const',
|
||||
'unsignedlong': 'unsigned long',
|
||||
'longlong': 'long long',
|
||||
'charconst': 'char const',
|
||||
};
|
||||
|
||||
export default function(str) {
|
||||
str = str.replace(/::_cxx\d\d::/g, '::').replace(/\s+/g, '');
|
||||
for (var key in _known_cpp_data) {
|
||||
if (str.indexOf(key) >= 0) {
|
||||
var find = new RegExp(key.replace(/([()[\]|\\*+])/g, '\\$1'), 'g');
|
||||
str = str.replace(find, _known_cpp_data[key]);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
/**
|
||||
* On rizin this can be done via afcfj <function name>
|
||||
* [
|
||||
* {
|
||||
* "name": "localtime",
|
||||
* "return": "struct tm *",
|
||||
* "count": 1,
|
||||
* "args": [
|
||||
* {
|
||||
* "name": "timer",
|
||||
* "type": "const time_t *"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
export default {
|
||||
'libc_start_main': {
|
||||
macro: [],
|
||||
required: 7
|
||||
},
|
||||
'exit': {
|
||||
macro: ['#include <stdlib.h>'],
|
||||
required: 1
|
||||
},
|
||||
'access': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 2
|
||||
},
|
||||
'fscanf': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 2,
|
||||
varargs: true
|
||||
},
|
||||
'fgets': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 3
|
||||
},
|
||||
'fclose': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 1
|
||||
},
|
||||
'fopen': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 2
|
||||
},
|
||||
'fwrite': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 4
|
||||
},
|
||||
'fread': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 4
|
||||
},
|
||||
'textdomain': {
|
||||
macro: ['#include <libintl.h>'],
|
||||
required: 2
|
||||
},
|
||||
'bindtextdomain': {
|
||||
macro: ['#include <libintl.h>'],
|
||||
required: 2
|
||||
},
|
||||
'setlocale': {
|
||||
macro: ['#include <locale.h>'],
|
||||
required: 2
|
||||
},
|
||||
'wcscmp': {
|
||||
macro: ['#include <wchar.h>'],
|
||||
required: 2
|
||||
},
|
||||
'strcmp': {
|
||||
macro: ['#include <string.h>'],
|
||||
required: 2
|
||||
},
|
||||
'strncmp': {
|
||||
macro: ['#include <string.h>'],
|
||||
required: 3
|
||||
},
|
||||
'msvcrt_dll_memset': {
|
||||
macro: ['#include <string.h>'],
|
||||
required: 3
|
||||
},
|
||||
'xmalloc': {
|
||||
macro: ['#include <stdlib.h>'],
|
||||
required: 1
|
||||
},
|
||||
'memset': {
|
||||
macro: ['#include <string.h>'],
|
||||
required: 3
|
||||
},
|
||||
'memcpy': {
|
||||
macro: ['#include <string.h>'],
|
||||
required: 3
|
||||
},
|
||||
'strcpy': {
|
||||
macro: ['#include <string.h>'],
|
||||
required: 2
|
||||
},
|
||||
'puts': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 1
|
||||
},
|
||||
'fprintf': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 2,
|
||||
varargs: true
|
||||
},
|
||||
'printf': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 1,
|
||||
varargs: true
|
||||
},
|
||||
'scanf': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: -1,
|
||||
varargs: true
|
||||
},
|
||||
'getenv': {
|
||||
macro: ['#include <stdlib.h>'],
|
||||
required: 1
|
||||
},
|
||||
'time': {
|
||||
macro: ['#include <time.h>'],
|
||||
required: 1
|
||||
},
|
||||
'localtime': {
|
||||
macro: ['#include <time.h>'],
|
||||
required: 1
|
||||
},
|
||||
'dcgettext': {
|
||||
macro: ['#include <libintl.h>'],
|
||||
required: 2
|
||||
},
|
||||
'pthread_create': {
|
||||
macro: ['#include <pthread.h>'],
|
||||
required: 4,
|
||||
},
|
||||
'pthread_join': {
|
||||
macro: ['#include <pthread.h>'],
|
||||
required: 2,
|
||||
},
|
||||
'pthread_exit': {
|
||||
macro: ['#include <pthread.h>'],
|
||||
required: 1,
|
||||
},
|
||||
'pthread_cancel': {
|
||||
macro: ['#include <pthread.h>'],
|
||||
required: 1,
|
||||
},
|
||||
'pthread_attr_init': {
|
||||
macro: ['#include <pthread.h>'],
|
||||
required: 1,
|
||||
},
|
||||
'pthread_attr_destroy': {
|
||||
macro: ['#include <pthread.h>'],
|
||||
required: 1,
|
||||
},
|
||||
'socket': {
|
||||
macro: ['#include <pthread.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'accept': {
|
||||
macro: ['#include <sys/socket.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'bind': {
|
||||
macro: ['#include <sys/socket.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'connect': {
|
||||
macro: ['#include <sys/socket.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'getsockname': {
|
||||
macro: ['#include <sys/socket.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'listen': {
|
||||
macro: ['#include <sys/socket.h>'],
|
||||
required: 2,
|
||||
},
|
||||
'recv': {
|
||||
macro: ['#include <sys/socket.h>'],
|
||||
required: 2,
|
||||
},
|
||||
'recvfrom': {
|
||||
macro: ['#include <sys/socket.h>'],
|
||||
required: 6,
|
||||
},
|
||||
'recvmsg': {
|
||||
macro: ['#include <sys/socket.h>'],
|
||||
required: 2,
|
||||
},
|
||||
'open': {
|
||||
macro: ['#include <unistd.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'creat': {
|
||||
macro: ['#include <unistd.h>'],
|
||||
required: 2,
|
||||
},
|
||||
'close': {
|
||||
macro: ['#include <unistd.h>'],
|
||||
required: 1,
|
||||
},
|
||||
'read': {
|
||||
macro: ['#include <unistd.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'write': {
|
||||
macro: ['#include <unistd.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'daemon': {
|
||||
macro: ['#include <unistd.h>'],
|
||||
required: 2,
|
||||
},
|
||||
'perror': {
|
||||
macro: ['#include <stdio.h>'],
|
||||
required: 1,
|
||||
},
|
||||
'shmget': {
|
||||
macro: ['#include <sys/shm.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'shmat': {
|
||||
macro: ['#include <sys/shm.h>'],
|
||||
required: 3,
|
||||
},
|
||||
'signal': {
|
||||
macro: ['#include <signal.h>'],
|
||||
required: 1,
|
||||
},
|
||||
/* iOS */
|
||||
'UIApplicationMain': {
|
||||
macro: [],
|
||||
required: 4,
|
||||
},
|
||||
'NSStringFromClass': {
|
||||
macro: [],
|
||||
required: 1,
|
||||
},
|
||||
'NSLog': {
|
||||
macro: [],
|
||||
required: 1,
|
||||
varargs: true
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import core from './core.js';
|
||||
import archs from './archs.js';
|
||||
import context from './context.js';
|
||||
|
||||
export default {
|
||||
core: core,
|
||||
archs: archs,
|
||||
context: context,
|
||||
supported: function() {
|
||||
return 'Supported architectures:\n ' + Object.keys(this.archs).join(', ');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
// SPDX-FileCopyrightText: 2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
function to64(n, unsigned) {
|
||||
if (unsigned) {
|
||||
return BigInt.asUintN(64, n);
|
||||
}
|
||||
return BigInt.asIntN(64, n);
|
||||
}
|
||||
|
||||
function toNum(num, unsigned) {
|
||||
const itype = typeof num;
|
||||
if (itype === 'bigint') {
|
||||
return num;
|
||||
} else if (itype === 'number') {
|
||||
const isNeg = num < 0;
|
||||
if (isNeg) {
|
||||
num = -num;
|
||||
}
|
||||
num = BigInt(num);
|
||||
if (isNeg) {
|
||||
num = 0n - num;
|
||||
}
|
||||
return BigInt.asIntN(64, num);
|
||||
} else if (Long.isLong(num)) {
|
||||
return num.value;
|
||||
}
|
||||
throw new Error("Unexpected type: " + (typeof num));
|
||||
}
|
||||
|
||||
function Long(value, isUnsigned) {
|
||||
this.unsigned = isUnsigned;
|
||||
this.value = to64(value, isUnsigned);
|
||||
this.isLong = true;
|
||||
this.toString = function(x) { return this.value.toString(x); };
|
||||
this.add = function(x) { return new Long(this.value + toNum(x, this.unsigned), this.unsigned); };
|
||||
this.sub = function(x) { return new Long(this.value - toNum(x, this.unsigned), this.unsigned); };
|
||||
this.shl = function(x) { return new Long(this.value << toNum(x, this.unsigned), this.unsigned); };
|
||||
this.shru = function(x) { return new Long(this.value >> toNum(x, this.unsigned), this.unsigned); };
|
||||
this.and = function(x) { return new Long(this.value & toNum(x, this.unsigned), this.unsigned); };
|
||||
this.or = function(x) { return new Long(this.value | toNum(x, this.unsigned), this.unsigned); };
|
||||
this.xor = function(x) { return new Long(this.value ^ toNum(x, this.unsigned), this.unsigned); };
|
||||
this.not = function() { return new Long(!this.value, this.unsigned); };
|
||||
this.eq = function(x) { return this.value == toNum(x, this.unsigned); };
|
||||
this.ne = function(x) { return this.value != toNum(x, this.unsigned); };
|
||||
this.lt = function(x) { return this.value < toNum(x, this.unsigned); };
|
||||
this.gt = function(x) { return this.value > toNum(x, this.unsigned); };
|
||||
this.lte = function(x) { return this.value <= toNum(x, this.unsigned); };
|
||||
this.gte = function(x) { return this.value >= toNum(x, this.unsigned); };
|
||||
}
|
||||
|
||||
Long.isLong = function(value) {
|
||||
return value && typeof value == "object" && value.isLong;
|
||||
};
|
||||
|
||||
Long.from = function(input, isUnsigned, base) {
|
||||
const itype = typeof input;
|
||||
if (itype === 'bigint') {
|
||||
return new Long(input, isUnsigned);
|
||||
} else if (itype === 'number') {
|
||||
let value = BigInt(input);
|
||||
return new Long(value, isUnsigned);
|
||||
} else if (itype === 'string') {
|
||||
if (input.length < 1) {
|
||||
return isUnsigned ? Long.UZERO : Long.ZERO;
|
||||
}
|
||||
const isNeg = input.startsWith("-");
|
||||
if (isNeg) {
|
||||
input = input.substr(1);
|
||||
}
|
||||
if (base == 16 && !input.startsWith('0x')) {
|
||||
input = "0x" + input;
|
||||
}
|
||||
let value = BigInt(input);
|
||||
if (isNeg) {
|
||||
value = 0n - value;
|
||||
}
|
||||
return new Long(value, isUnsigned);
|
||||
} else if (Long.isLong(input)) {
|
||||
return input;
|
||||
}
|
||||
throw new Error("Invalid type for Long (" + itype + ') with args ' + [...arguments].join(', '));
|
||||
};
|
||||
|
||||
Long.ZERO = Long.from(0, false);
|
||||
Long.UZERO = Long.from(0, true);
|
||||
Long.MAX_U64_VALUE = new Long(Limits.UT64_MAX, true);
|
||||
Long.MAX_U32_VALUE = new Long(Limits.UT32_MAX, true);
|
||||
Long.MAX_U16_VALUE = new Long(Limits.UT16_MAX, true);
|
||||
Long.MAX_S64_VALUE = new Long(Limits.ST64_MAX, false);
|
||||
Long.MAX_S32_VALUE = new Long(Limits.ST32_MAX, false);
|
||||
Long.MAX_S16_VALUE = new Long(Limits.ST16_MAX, false);
|
||||
|
||||
|
||||
export default Long;
|
||||
@@ -0,0 +1,183 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import JSONex from './JSONex.js';
|
||||
import Long from './long.js';
|
||||
import Anno from './annotation.js';
|
||||
import ColorsAnsi from './colors/ansi.js';
|
||||
import ColorsInvalid from './colors/invalid.js';
|
||||
|
||||
function initializeColors() {
|
||||
const config = {};
|
||||
const colors = rizin ? rizin.command("ec*") : '';
|
||||
colors.split('\n').forEach(function(line) {
|
||||
const tok = line.split(' ');
|
||||
config[tok[1]] = tok[2];
|
||||
});
|
||||
return {
|
||||
"callname": config['call'] || 'gray',
|
||||
"integers": config['num'] || 'cyan',
|
||||
"comment": config['comment'] || 'red',
|
||||
"labels": config['flag'] || 'green',
|
||||
"types": config['func_var_type'] || 'green',
|
||||
"macro": config['cjmp'] || 'yellow',
|
||||
"flow": config['flow'] || 'magenta',
|
||||
"text": config['usrcmt'] || 'yellow',
|
||||
};
|
||||
}
|
||||
//const _autoregex = /(\bif\b|\belse\b|\bwhile\b|\bfor\b|\bdo\b|\breturn\b|[ui]+nt[123468]+\_t|\bvoid\b|\bconst\b|\bsizeof\b|\bfloat\b|\bdouble\b|0x[0-9A-Fa-f]+|\b\d+\b)/g
|
||||
|
||||
/**
|
||||
* Contains the regex used to colorize a given string.
|
||||
* @type {Object}
|
||||
*/
|
||||
const _autotheme = {
|
||||
controlflow: /\bif\b|\belse\b|\bwhile\b|\bfor\b|\bdo\b|\breturn\b|\bthrow\b/g,
|
||||
definebits: /[ui]+nt[123468]+_t|\bvoid\b|\bconst\b|\bsizeof\b|\bfloat\b|\bdouble\b|\bchar\b|\bwchar_t\b|\bextern\b|\bstruct\b|\bsize_t\b|\btime_t\b|\bboolean\b/g,
|
||||
numbers: /0x[0-9a-fA-F]+|\b(?![;[])\d+(?![;m])\b/g,
|
||||
string: /("[^"]+")/,
|
||||
};
|
||||
|
||||
const defaulttheme = initializeColors();
|
||||
|
||||
/**
|
||||
* Color types (ansi, nocolor)
|
||||
* @type {Object}
|
||||
*/
|
||||
const Colors = {
|
||||
ansi: ColorsAnsi,
|
||||
text: ColorsInvalid,
|
||||
};
|
||||
|
||||
var _theme_colors = Colors.text.make(defaulttheme);
|
||||
|
||||
/**
|
||||
* Applies colors to the string given a certain regex
|
||||
* @param {String} input - Input string
|
||||
* @param {String} type - Color name (ansi, text)
|
||||
* @param {String} regex - Regex to apply
|
||||
* @return {String} - Result string
|
||||
*/
|
||||
var _apply_regex = function(input, type, regex) {
|
||||
var x = input.split(regex);
|
||||
var p = input.match(regex);
|
||||
var s = '';
|
||||
if (!p) {
|
||||
return s;
|
||||
}
|
||||
var i = 0;
|
||||
for (i = 0; i < p.length; i++) {
|
||||
if (p[i].length > 0) {
|
||||
s += x[i] + _theme_colors[type](p[i]);
|
||||
} else {
|
||||
s += x[i] + p[i];
|
||||
}
|
||||
}
|
||||
for (; i < x.length; i++) {
|
||||
s += x[i];
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
/**
|
||||
* Colorize the input string via ansi converters
|
||||
* @param {String} input - Input string
|
||||
* @return {String} - Result string
|
||||
*/
|
||||
var _colorize_text = function(input) {
|
||||
if (!input || input.length < 1) {
|
||||
return '';
|
||||
}
|
||||
if (!Global().evars.honor.color) {
|
||||
return input;
|
||||
}
|
||||
/* control flow (if, else, while, do, etc..) */
|
||||
var x = input.split(_autotheme.controlflow);
|
||||
if (x.length > 1) {
|
||||
input = _apply_regex(input, 'flow', _autotheme.controlflow);
|
||||
}
|
||||
/* numbers */
|
||||
x = input.split(_autotheme.numbers);
|
||||
if (x.length == 1 && x == '') {
|
||||
input = this.theme.integers(input);
|
||||
} else if (x.length > 1) {
|
||||
input = _apply_regex(input, 'integers', _autotheme.numbers);
|
||||
}
|
||||
/* uint32_t, etc.. */
|
||||
x = input.split(_autotheme.definebits);
|
||||
if (x.length == 1 && x == '') {
|
||||
input = this.theme.types(input);
|
||||
} else if (input.split(_autotheme.definebits).length > 1) {
|
||||
input = _apply_regex(input, 'types', _autotheme.definebits);
|
||||
}
|
||||
return input;
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies all the colors options (theme/colors).
|
||||
*/
|
||||
var _get_theme = function(evars) {
|
||||
if (evars.honor && evars.honor.color) {
|
||||
return Colors.ansi.make(defaulttheme);
|
||||
}
|
||||
return Colors.text.make(defaulttheme);
|
||||
};
|
||||
|
||||
/**
|
||||
* Prints the final jsdec output
|
||||
* @param useJSON - boolean whether to print as json
|
||||
*/
|
||||
var _flush_output = function(lines, errors, log) {
|
||||
if (Global().evars.extra.annotation && lines) {
|
||||
var actx = Anno.context();
|
||||
var res = {
|
||||
code: "",
|
||||
annotations: []
|
||||
};
|
||||
lines.forEach(function(x) {
|
||||
var parsed = Anno.parse(res.code.length, x, actx);
|
||||
res.annotations = res.annotations.concat(parsed);
|
||||
res.code += x.str + '\n';
|
||||
});
|
||||
console.log(JSONex.stringify(res));
|
||||
} else if (Global().evars.extra.json) {
|
||||
var jdata = {};
|
||||
if (lines && lines.length > 0) {
|
||||
jdata.lines = lines;
|
||||
}
|
||||
if (errors && errors.length > 0) {
|
||||
jdata.errors = errors;
|
||||
}
|
||||
if (log && log.length > 0) {
|
||||
jdata.log = log;
|
||||
}
|
||||
console.log(JSONex.stringify(jdata));
|
||||
} else {
|
||||
var prefix = Global().evars.extra.allfunctions ? "// " : "";
|
||||
if (lines && lines.length > 0) {
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (Global().evars.extra.highlights && Global().evars.extra.offset.eq(lines[i].offset || Long.ZERO)) {
|
||||
console.log('\u001b[7m' + lines[i].str + '\u001b[49m\u001b[0m');
|
||||
} else {
|
||||
console.log(lines[i].str);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (errors && errors.length > 0) {
|
||||
console.log(prefix + errors.join("\n").replace(/\n/g, '\n' + prefix));
|
||||
}
|
||||
if (log && log.length > 0) {
|
||||
console.log(prefix + log.join("\n").replace(/\n/g, '\n' + prefix));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Printer Object
|
||||
* @return {Function} - Printer object (to be called via `new Printer()`)
|
||||
*/
|
||||
export default function() {
|
||||
this.theme = _get_theme(Global().evars);
|
||||
this.auto = _colorize_text;
|
||||
this.flushOutput = _flush_output;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// SPDX-FileCopyrightText: 2019-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import JSONex from './JSONex.js';
|
||||
import Long from './long.js';
|
||||
|
||||
function rzcustom(value, regex, function_fix) {
|
||||
var x = rizin.command(value) || "";
|
||||
if (regex) {
|
||||
x = x.replace(regex, '');
|
||||
}
|
||||
return function_fix ? function_fix(x.trim()) : x.trim();
|
||||
}
|
||||
|
||||
function rzstr(value, multiline) {
|
||||
var x = rizin.command(value) || "";
|
||||
if (multiline) {
|
||||
x = x.replace(/\n/g, '');
|
||||
}
|
||||
return x.trim();
|
||||
}
|
||||
|
||||
function rzjson(m, def) {
|
||||
var x = rzstr(m, true);
|
||||
try {
|
||||
return x.length > 0 ? JSONex.parse(x) : def;
|
||||
} catch(e){}
|
||||
return def;
|
||||
}
|
||||
|
||||
function rzint(value, def) {
|
||||
var x = rzstr(value);
|
||||
if (x != '') {
|
||||
try {
|
||||
return parseInt(x);
|
||||
} catch (e) {}
|
||||
}
|
||||
return def || 0;
|
||||
}
|
||||
|
||||
function rzlong(value, def) {
|
||||
var x = rzstr(value);
|
||||
if (x != '') {
|
||||
try {
|
||||
return Long.from(x, true);
|
||||
} catch (e) {}
|
||||
}
|
||||
return def || Long.UZERO;
|
||||
}
|
||||
|
||||
function rzbool(value) {
|
||||
var x = rzstr(value);
|
||||
return x == 'true' || x == '1';
|
||||
}
|
||||
|
||||
export default {
|
||||
custom: rzcustom,
|
||||
string: rzstr,
|
||||
json: rzjson,
|
||||
int: rzint,
|
||||
long: rzlong,
|
||||
bool: rzbool,
|
||||
};
|
||||
@@ -0,0 +1,256 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import rzpipe from './rzpipe.js';
|
||||
import JSONex from './JSONex.js';
|
||||
import libdec from './libdec.js';
|
||||
|
||||
export default (function() {
|
||||
var __line_cnt = 0;
|
||||
|
||||
function rz_sanitize(value, expected) {
|
||||
return value.length == 0 ? expected : value;
|
||||
}
|
||||
|
||||
function jsdec_sanitize(enable, evar, oldstatus, newstatus) {
|
||||
if (enable) {
|
||||
rizin.command('e ' + evar + '=' + newstatus);
|
||||
} else {
|
||||
rizin.command('e ' + evar + '=' + oldstatus);
|
||||
}
|
||||
}
|
||||
|
||||
function merge_arrays(input) {
|
||||
input = input.replace(/\n/g, ',');
|
||||
var array = '[' + input + ']';
|
||||
return array;
|
||||
}
|
||||
|
||||
function merge_arrays_json(input) {
|
||||
return JSONex.parse(merge_arrays(input));
|
||||
}
|
||||
|
||||
var padding = ' ';
|
||||
var usages = {
|
||||
"--help": "this help message",
|
||||
"--architectures": "lists the supported architectures",
|
||||
"--all-functions": "decompile all functions",
|
||||
"--assembly": "shows pseudo next to the assembly",
|
||||
"--blocks": "shows only scopes blocks",
|
||||
"--casts": "shows all casts in the pseudo code",
|
||||
"--colors": "enables syntax colors",
|
||||
"--debug": "do not catch exceptions",
|
||||
"--issue": "generates the json used for the test suite",
|
||||
"--offsets": "shows pseudo next to the assembly offset",
|
||||
"--paddr": "all xrefs uses physical addresses instead of virtual addresses",
|
||||
"--xrefs": "shows also instruction xrefs in the pseudo code",
|
||||
"--highlight-current": "highlights the current address.",
|
||||
"--as-comment": "the decompiled code is returned to r2 as comment (via CCu)",
|
||||
"--as-code-line": "the decompiled code is returned to r2 as 'file:line code' (via CL)",
|
||||
"--as-json": "the decompiled code lines are returned as JSON",
|
||||
"--annotation": "the decompiled code lines are returned with the annotation format"
|
||||
};
|
||||
|
||||
function has_option(args, name) {
|
||||
return (args.indexOf(name) >= 0);
|
||||
}
|
||||
|
||||
function has_invalid_args(args) {
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
if (args[i] != '' && !usages[args[i]]) {
|
||||
console.log('Invalid argument \'' + args[i] + '\'\n');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function usage() {
|
||||
console.log("jsdec [options]");
|
||||
for (var key in usages) {
|
||||
var cmd = key + padding.substr(key.length, padding.length);
|
||||
console.log(" " + cmd + " | " + usages[key]);
|
||||
}
|
||||
}
|
||||
|
||||
function print_issue() {
|
||||
var xrefs = rz_sanitize(rzpipe.string('isj'), '[]');
|
||||
var strings = rz_sanitize(rzpipe.string('Cslj'), '[]');
|
||||
var functions = rz_sanitize(rzpipe.string('aflj'), '[]');
|
||||
var classes = rz_sanitize(rzpipe.string('icj'), '[]');
|
||||
var data = JSONex.stringify(rizin.graph());
|
||||
var farguments = rz_sanitize(rzpipe.string('afvlj', true), '{"stack":[],"reg":[]}');
|
||||
var arch = rz_sanitize(rzpipe.string('e asm.arch'), '');
|
||||
var archbits = rz_sanitize(rzpipe.string('e asm.bits'), '32');
|
||||
var database = rz_sanitize(rzpipe.custom('afsj @@i', null, merge_arrays), '[]');
|
||||
console.log('{"name":"issue_' + (new Date()).getTime() +
|
||||
'","arch":"' + arch +
|
||||
'","archbits":' + archbits +
|
||||
',"graph":' + data +
|
||||
',"isj":' + xrefs +
|
||||
',"Csj":' + strings +
|
||||
',"icj":' + classes +
|
||||
',"afvj":' + farguments +
|
||||
',"afcfj":' + database +
|
||||
',"aflj":' + functions + '}');
|
||||
}
|
||||
|
||||
function print_archs() {
|
||||
var archs = Object.keys(libdec.archs);
|
||||
archs.sort();
|
||||
console.log('Supported architectures: ' + archs.join(', '));
|
||||
}
|
||||
|
||||
var rzutil = {
|
||||
check_args: function(args) {
|
||||
if (has_invalid_args(args)) {
|
||||
args.push('--help');
|
||||
}
|
||||
if (has_option(args, '--help')) {
|
||||
usage();
|
||||
return true;
|
||||
}
|
||||
if (has_option(args, '--issue')) {
|
||||
print_issue();
|
||||
return true;
|
||||
}
|
||||
if (has_option(args, '--architectures')) {
|
||||
print_archs();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
evars: function(args) {
|
||||
let o = {};
|
||||
o.arch = rzpipe.string('e asm.arch');
|
||||
o.archbits = rzpipe.int('e asm.bits', 32);
|
||||
o.honor = {
|
||||
casts: rzpipe.bool('e jsdec.casts') || has_option(args, '--casts'),
|
||||
assembly: rzpipe.bool('e jsdec.asm') || has_option(args, '--assembly'),
|
||||
blocks: rzpipe.bool('e jsdec.blocks') || has_option(args, '--blocks'),
|
||||
xrefs: rzpipe.bool('e jsdec.xrefs') || has_option(args, '--xrefs'),
|
||||
paddr: rzpipe.bool('e jsdec.paddr') || has_option(args, '--paddr'),
|
||||
offsets: has_option(args, '--offsets'),
|
||||
color: rzpipe.int('e scr.color', 0) > 0 || has_option(args, '--colors')
|
||||
};
|
||||
o.sanitize = {
|
||||
ucase: rzpipe.bool('e asm.ucase'),
|
||||
pseudo: rzpipe.bool('e asm.pseudo'),
|
||||
capitalize: rzpipe.bool('e asm.capitalize'),
|
||||
html: rzpipe.bool('e scr.html'),
|
||||
syntax: rzpipe.string('e asm.syntax'),
|
||||
};
|
||||
o.extra = {
|
||||
allfunctions: has_option(args, '--all-functions'),
|
||||
ascodeline: has_option(args, '--as-code-line'),
|
||||
ascomment: has_option(args, '--as-comment'),
|
||||
debug: rzpipe.bool('e jsdec.debug') || has_option(args, '--debug'),
|
||||
file: rzpipe.string('i~^file[1:0]'),
|
||||
highlights: rzpipe.bool('e jsdec.highlight') || has_option(args, '--highlight-current'),
|
||||
json: has_option(args, '--as-json'),
|
||||
offset: rzpipe.long('s'),
|
||||
slow: rzpipe.bool('e jsdec.slow'),
|
||||
annotation: has_option(args, '--annotation'),
|
||||
};
|
||||
o.add_comment = function(comment, offset) {
|
||||
if (!comment || comment.length < 1) {
|
||||
return;
|
||||
}
|
||||
rizin.command('CC- @ 0x' + offset.toString(16));
|
||||
rizin.command('CCu base64:' + btoa(comment) + ' @ 0x' + offset.toString(16));
|
||||
};
|
||||
o.add_code_line = function(comment, offset) {
|
||||
if (comment.trim().length < 1) {
|
||||
return;
|
||||
}
|
||||
var line = __line_cnt++;
|
||||
rizin.command('"CL 0x' + offset.toString(16) + ' jsdec.c:' + line + ' ' + comment.replace(/\n/g, '; ').replace(/\\/g, '\\\\').replace(/"/g, '\\"') + ';"');
|
||||
};
|
||||
|
||||
if (o.extra.ascomment || o.extra.ascodeline) {
|
||||
o.honor.assembly = false;
|
||||
o.honor.blocks = false;
|
||||
o.honor.offsets = false;
|
||||
o.extra.json = false;
|
||||
o.honor.color = false;
|
||||
o.extra.highlights = false;
|
||||
o.extra.annotation = false;
|
||||
}
|
||||
|
||||
if (o.extra.allfunctions) {
|
||||
o.extra.ascomment = false;
|
||||
o.extra.ascodeline = false;
|
||||
o.honor.assembly = false;
|
||||
o.honor.blocks = false;
|
||||
o.honor.offsets = false;
|
||||
o.extra.json = false;
|
||||
o.extra.highlights = false;
|
||||
o.extra.annotation = false;
|
||||
}
|
||||
|
||||
if (o.extra.annotation) {
|
||||
o.extra.ascodeline = false;
|
||||
o.extra.ascomment = false;
|
||||
o.extra.highlights = false;
|
||||
o.extra.html = false;
|
||||
o.extra.json = false;
|
||||
o.honor.assembly = false;
|
||||
o.honor.blocks = false;
|
||||
o.honor.color = false;
|
||||
o.honor.offsets = false;
|
||||
}
|
||||
|
||||
if (o.sanitize.html || !o.honor.color) {
|
||||
o.extra.highlights = false;
|
||||
}
|
||||
return o;
|
||||
},
|
||||
data: function(isfast) {
|
||||
if (isfast === undefined) {
|
||||
isfast = !rzpipe.bool('e jsdec.slow');
|
||||
}
|
||||
this.arch = rzpipe.string('e asm.arch');
|
||||
this.bits = rzpipe.int('e asm.bits', 32);
|
||||
this.xrefs = {
|
||||
symbols: (isfast ? [] : rzpipe.json('isj', [])),
|
||||
strings: (isfast ? [] : rzpipe.json('Cslj', [])),
|
||||
functions: (isfast ? [] : rzpipe.json('aflj', [])),
|
||||
classes: rzpipe.json('icj', []),
|
||||
arguments: rzpipe.json('afvlj', {
|
||||
"stack": [],
|
||||
"reg": []
|
||||
})
|
||||
};
|
||||
this.xrefs.arguments.stack = this.xrefs.arguments.stack || [];
|
||||
this.xrefs.arguments.reg = this.xrefs.arguments.reg || [];
|
||||
this.graph = rizin.graph();
|
||||
this.argdb = rzpipe.custom('afsj @@i', null, merge_arrays_json);
|
||||
},
|
||||
sanitize: function(enable, evars) {
|
||||
if (!evars) {
|
||||
return;
|
||||
}
|
||||
var s = evars.sanitize;
|
||||
jsdec_sanitize(enable, 'asm.ucase', s.ucase, 'false');
|
||||
jsdec_sanitize(enable, 'asm.pseudo', s.pseudo, 'false');
|
||||
jsdec_sanitize(enable, 'asm.capitalize', s.capitalize, 'false');
|
||||
jsdec_sanitize(enable, 'scr.html', s.html, 'false');
|
||||
if (evars.arch == 'x86') {
|
||||
jsdec_sanitize(enable, 'asm.syntax', s.syntax, 'intel');
|
||||
}
|
||||
},
|
||||
debug: function(evars, exception) {
|
||||
rzutil.sanitize(false, evars);
|
||||
if (!evars || evars.extra.debug) {
|
||||
var msg = exception.message + ' (' + exception.name + ')';
|
||||
return 'Exception: ' + msg + '\n' + exception.stack;
|
||||
} else {
|
||||
return '\n\njsdec has crashed (info: ' + rzpipe.string('i~^file[1:0]') + ' @ ' + rzpipe.string('s') + ').\n' +
|
||||
'Please report the bug at https://github.com/rizinorg/jsdec/issues\n' +
|
||||
'Use the option \'--issue\' or the command \'pddi\' to generate \n' +
|
||||
'the needed data for the issue.';
|
||||
}
|
||||
}
|
||||
};
|
||||
return rzutil;
|
||||
})();
|
||||
@@ -0,0 +1,80 @@
|
||||
// SPDX-FileCopyrightText: 2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import Long from './long.js';
|
||||
import JSONex from './JSONex.js';
|
||||
|
||||
function _new_evars(issue) {
|
||||
let evars = {};
|
||||
evars.arch = issue.arch;
|
||||
evars.archbits = issue.bits;
|
||||
evars.honor = {
|
||||
assembly: true,
|
||||
blocks: false,
|
||||
casts: true,
|
||||
offsets: false,
|
||||
paddr: false,
|
||||
pseudo: false,
|
||||
xrefs: false,
|
||||
};
|
||||
evars.extra = {
|
||||
allfuncs: false,
|
||||
ascodeline: false,
|
||||
ascomment: false,
|
||||
debug: true,
|
||||
file: 'testsuite',
|
||||
highlights: false,
|
||||
offset: Long.UZERO,
|
||||
slow: true,
|
||||
theme: 'default',
|
||||
annotation: false,
|
||||
};
|
||||
return evars;
|
||||
}
|
||||
|
||||
// old graph has base64 encoded comments.
|
||||
function _fixup_old_graph(graph) {
|
||||
graph[0].blocks.forEach((block) => {
|
||||
block.ops.forEach((instr) => {
|
||||
instr.comment = instr.comment ? atob(instr.comment) : undefined;
|
||||
});
|
||||
});
|
||||
return graph;
|
||||
}
|
||||
|
||||
function _new_data(issue) {
|
||||
if (!issue.arch) {
|
||||
throw new Error('missing architecture in JSON.');
|
||||
}
|
||||
let bits = issue.archbits || issue.bits || 32;
|
||||
if (bits) {
|
||||
// if bits is in the issue then it has been decoded as a Long object.
|
||||
// to override this is required to be converted to just an integer.
|
||||
bits = parseInt(bits.toString());
|
||||
}
|
||||
var testdata = {
|
||||
arch: issue.arch,
|
||||
bits: bits,
|
||||
graph: issue.agj ? _fixup_old_graph(issue.agj) : (issue.graph || []),
|
||||
xrefs: {
|
||||
symbols: issue.isj || [],
|
||||
strings: issue.Csj || issue.izj || [],
|
||||
functions: issue.aflj || [],
|
||||
classes: issue.icj || [],
|
||||
arguments: issue.afvj || {
|
||||
"stack": [],
|
||||
"reg": []
|
||||
}
|
||||
},
|
||||
argdb: issue.afcfj
|
||||
};
|
||||
testdata.xrefs.arguments.stack = testdata.xrefs.arguments.stack || [];
|
||||
testdata.xrefs.arguments.reg = testdata.xrefs.arguments.reg || [];
|
||||
return testdata;
|
||||
}
|
||||
|
||||
export default function(filename) {
|
||||
const issue = JSONex.parse(unit.raw);
|
||||
this.data = _new_data(issue);
|
||||
this.evars = _new_evars(this.data);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// SPDX-FileCopyrightText: 2018-2023 Giovanni Dante Grazioli <[email protected]>
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
export default function(message) {
|
||||
if (typeof message == 'string') {
|
||||
message = "[!] " + message;
|
||||
if (this.printer.theme.comment) {
|
||||
message = this.printer.theme.comment(message);
|
||||
}
|
||||
this.context.printLog(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# SPDX-FileCopyrightText: 2023 Giovanni Dante Grazioli <[email protected]>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
jsdec_plugin = 'jsdec-plugin.js'
|
||||
jsdec_testsuite = 'jsdec-testsuite.js'
|
||||
|
||||
if get_option('build_type') == 'standalone'
|
||||
bytecode_h = custom_target(
|
||||
'bytecode.h',
|
||||
build_always_stale: true,
|
||||
output : 'bytecode.h',
|
||||
input : jsdec_testsuite,
|
||||
command : [qjsc, '-m', '-N', 'main_bytecode', '-o', '@OUTPUT@', '@INPUT@'],
|
||||
)
|
||||
else
|
||||
bytecode_h = custom_target(
|
||||
'bytecode.h',
|
||||
build_always_stale: true,
|
||||
output : 'bytecode.h',
|
||||
input : jsdec_plugin,
|
||||
command : [qjsc, '-m', '-N', 'main_bytecode', '-o', '@OUTPUT@', '@INPUT@'],
|
||||
)
|
||||
endif
|
||||
|
||||
bytecode_mod_h = custom_target(
|
||||
'bytecode_mod.h',
|
||||
build_always_stale: true,
|
||||
output : 'bytecode_mod.h',
|
||||
input : bytecode_h,
|
||||
command : [modjs_gen, '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, pancake <[email protected]>, Giovanni Dante Grazioli <[email protected]>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Global data accessible from everywhere.
|
||||
* @type {Object}
|
||||
*/
|
||||
var Global = {
|
||||
context: null,
|
||||
evars: null,
|
||||
printer: null,
|
||||
argdb: null,
|
||||
warning: require('libdec/warning')
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
var libdec = require('libdec/libdec');
|
||||
var rzutil = require('libdec/rzutil');
|
||||
var rzpipe = require('libdec/rzpipe');
|
||||
|
||||
function decompile_offset(architecture, fcnname) {
|
||||
var data = new rzutil.data();
|
||||
Global.argdb = data.argdb;
|
||||
// af seems to break renaming.
|
||||
/* asm.pseudo breaks things.. */
|
||||
if (data.graph && data.graph.length > 0) {
|
||||
var p = new libdec.core.session(data, architecture);
|
||||
var arch_context = architecture.context(data);
|
||||
libdec.core.analysis.pre(p, architecture, arch_context);
|
||||
libdec.core.decompile(p, architecture, arch_context);
|
||||
libdec.core.analysis.post(p, architecture, arch_context);
|
||||
libdec.core.print(p);
|
||||
} else if (Global.evars.extra.allfunctions) {
|
||||
Global.context.printLog('Error: Please analyze the ' + fcnname + ' first.', true);
|
||||
} else {
|
||||
Global.context.printLog('Error: no data available.\nPlease analyze the function/binary first.', true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* jsdec main function.
|
||||
* @param {Array} args - jsdec arguments to be used to configure the output.
|
||||
*/
|
||||
function jsdec_main(args) { // lgtm [js/unused-local-variable]
|
||||
var Printer = require('libdec/printer');
|
||||
var lines = null;
|
||||
var errors = [];
|
||||
var log = [];
|
||||
try {
|
||||
Global.evars = new rzutil.evars(args);
|
||||
rzutil.sanitize(true, Global.evars);
|
||||
if (rzutil.check_args(args)) {
|
||||
rzutil.sanitize(false, Global.evars);
|
||||
return;
|
||||
}
|
||||
|
||||
// theme (requires to be initialized after evars)
|
||||
Global.printer = new Printer();
|
||||
|
||||
var architecture = libdec.archs[Global.evars.arch];
|
||||
|
||||
if (architecture) {
|
||||
Global.context = new libdec.context();
|
||||
if (Global.evars.extra.allfunctions) {
|
||||
var current = rzpipe.string('s');
|
||||
var functions = rzpipe.json64('aflj');
|
||||
functions.forEach(function(x) {
|
||||
if (x.name.startsWith('sym.imp.') || x.name.startsWith('loc.imp.')) {
|
||||
return;
|
||||
}
|
||||
rzpipe.string('s 0x' + x.offset.toString(16));
|
||||
Global.context.printLine("");
|
||||
Global.context.printLine(Global.printer.theme.comment('/* name: ' + x.name + ' @ 0x' + x.offset.toString(16) + ' */'));
|
||||
decompile_offset(architecture, x.name);
|
||||
});
|
||||
rzpipe.string('s ' + current);
|
||||
var o = Global.context;
|
||||
Global.context = new libdec.context();
|
||||
Global.context.macros = o.macros;
|
||||
Global.context.dependencies = o.dependencies;
|
||||
Global.context.printLine(Global.printer.theme.comment('/* jsdec pseudo code output */'));
|
||||
Global.context.printLine(Global.printer.theme.comment('/* ' + Global.evars.extra.file + ' */'));
|
||||
if (['java', 'dalvik'].indexOf(Global.evars.arch) < 0) {
|
||||
Global.context.printMacros(true);
|
||||
Global.context.printDependencies(true);
|
||||
}
|
||||
o.lines = Global.context.lines.concat(o.lines);
|
||||
Global.context = o;
|
||||
} else {
|
||||
decompile_offset(architecture);
|
||||
}
|
||||
errors = errors.concat(Global.context.errors);
|
||||
log = log.concat(Global.context.log);
|
||||
lines = Global.context.lines;
|
||||
} else {
|
||||
errors.push(Global.evars.arch + ' is not currently supported.\n' +
|
||||
'Please open an enhancement issue at https://github.com/rizinorg/jsdec/issues\n' +
|
||||
libdec.supported());
|
||||
}
|
||||
rzutil.sanitize(false, Global.evars);
|
||||
} catch (e) {
|
||||
errors.push(rzutil.debug(Global.evars, e));
|
||||
}
|
||||
|
||||
var printer = Global.printer;
|
||||
if (!printer) {
|
||||
printer = new Printer();
|
||||
}
|
||||
printer.flushOutput(lines, errors, log, Global.evars.extra);
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Giovanni Dante Grazioli <[email protected]>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Global data accessible from everywhere.
|
||||
* @type {Object}
|
||||
*/
|
||||
var Global = {
|
||||
context: null,
|
||||
evars: null,
|
||||
printer: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
var libdec = require('libdec/libdec');
|
||||
var rzutil = require('libdec/rzutil');
|
||||
|
||||
const rzcmd = null;
|
||||
|
||||
/**
|
||||
* jsdec main function.
|
||||
* @param {String} filename - Issue filename to analyze (relative/fullpath)
|
||||
*/
|
||||
function jsdec_main(filename) { // lgtm [js/unused-local-variable]
|
||||
try {
|
||||
// imports
|
||||
var Printer = require('libdec/printer');
|
||||
if (filename && !rzcmd) {
|
||||
var jsonstr = read_file(filename).trim();
|
||||
var data = rzutil.dataTestSuite(jsonstr);
|
||||
Global.evars = new rzutil.evarsTestSuite(data);
|
||||
Global.argdb = data.argdb;
|
||||
Global.printer = new Printer();
|
||||
|
||||
var architecture = libdec.archs[data.arch];
|
||||
Global.context = new libdec.context();
|
||||
// af seems to break renaming.
|
||||
/* asm.pseudo breaks things.. */
|
||||
if (data.graph && data.graph.length > 0) {
|
||||
var p = new libdec.core.session(data, architecture);
|
||||
var arch_context = architecture.context(data);
|
||||
libdec.core.analysis.pre(p, architecture, arch_context);
|
||||
libdec.core.decompile(p, architecture, arch_context);
|
||||
libdec.core.analysis.post(p, architecture, arch_context);
|
||||
libdec.core.print(p);
|
||||
Global.printer.flushOutput(Global.context.lines, Global.context.errors, Global.context.log, Global.evars.extra);
|
||||
} else {
|
||||
console.log('Error: no data available.\nPlease analyze the function/binary first.');
|
||||
}
|
||||
} else {
|
||||
console.log('missing JSON to test.');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Exception:', e.stack);
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
const _re = {
|
||||
controlflow: /\bif\b|\belse\b|\bwhile\b|\bfor\b|\bdo\b|\breturn\b|\bthrow\b/g,
|
||||
definebits: /[ui]+nt[123468]+_t|\bvoid\b|\bconst\b|\bsizeof\b|\bfloat\b|\bdouble\b|\bchar\b|\bwchar_t\b|\bextern\b|\bstruct\b|\bsize_t\b|\btime_t\b|\bboolean\b/g,
|
||||
numbers: /0x[0-9a-fA-F]+|\b(?![;[])\d+(?![;m])\b/g,
|
||||
string: /"[^"]+"/,
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies colors to the string given a certain regex
|
||||
* @param {String} regex - Regex to apply
|
||||
* @param {String} input - Array of values
|
||||
* @param {String} type - Annotation type
|
||||
* @param {Number} offset - Annotation offset
|
||||
* @return {Object} - Result
|
||||
*/
|
||||
function _auto_annotation(regex, input, type, offset) {
|
||||
var x = input.split(regex);
|
||||
var p = input.match(regex);
|
||||
var s = [];
|
||||
if (!p) {
|
||||
return input;
|
||||
}
|
||||
var i = 0;
|
||||
for (i = 0; i < p.length; i++) {
|
||||
s.push(x[i]);
|
||||
if (p[i].length > 0) {
|
||||
s.push(new Annotation(p[i], offset, type));
|
||||
}
|
||||
}
|
||||
for (; i < x.length; i++) {
|
||||
s.push(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotation object
|
||||
* @param string value Value of the annotation
|
||||
* @param number location Location of the annotation
|
||||
* @param string type Type of the annotation
|
||||
*/
|
||||
function Annotation(value, location, type) {
|
||||
this.value = value || "";
|
||||
this.location = location || Global.evars.extra.offset;
|
||||
this.type = this.value.length > 0 ? type : 'offset';
|
||||
this._annotation_ = true;
|
||||
this.define = function(current) {
|
||||
var d = {
|
||||
start: current,
|
||||
end: current + this.value.length,
|
||||
type: this.type,
|
||||
};
|
||||
if (["function_name", "function_parameter", "local_variable"].indexOf(this.type) >= 0) {
|
||||
d.name = this.value;
|
||||
}
|
||||
if (["function_name", "offset"].indexOf(this.type) >= 0) {
|
||||
d.offset = this.location.toString();
|
||||
}
|
||||
return d;
|
||||
};
|
||||
this.syntax = function(current) {
|
||||
return {
|
||||
start: current,
|
||||
end: current + this.value.length,
|
||||
type: "syntax_highlight",
|
||||
syntax_highlight: this.type,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function _rebuild(array) {
|
||||
var newarray = [];
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
if (Array.isArray(array[i])) {
|
||||
newarray = newarray.concat(array[i]);
|
||||
} else {
|
||||
newarray.push(array[i]);
|
||||
}
|
||||
}
|
||||
return newarray;
|
||||
}
|
||||
|
||||
return {
|
||||
/* do some magic and autoassigne the values. */
|
||||
auto: function(value, location) {
|
||||
if (typeof(value) !== "string") {
|
||||
if (value && value.toAnnotation) {
|
||||
value = value.toAnnotation(location);
|
||||
} else if (value.__isLong__ || typeof (value) === 'number') {
|
||||
value = new Annotation('0x' + value.toString(16), location, "constant_variable");
|
||||
}
|
||||
return Array.isArray(value) ? _rebuild(value) : [value];
|
||||
}
|
||||
var i;
|
||||
var a = _auto_annotation(_re.string, value, "offset", location);
|
||||
for (i = 0; i < a.length; i++) {
|
||||
if (typeof(a[i]) === "string") {
|
||||
a[i] = _auto_annotation(_re.controlflow, a[i], "keyword", location);
|
||||
}
|
||||
}
|
||||
a = _rebuild(a);
|
||||
for (i = 0; i < a.length; i++) {
|
||||
if (typeof(a[i]) === "string") {
|
||||
a[i] = _auto_annotation(_re.numbers, a[i], "constant_variable", location);
|
||||
}
|
||||
}
|
||||
a = _rebuild(a);
|
||||
for (i = 0; i < a.length; i++) {
|
||||
if (typeof(a[i]) === "string") {
|
||||
a[i] = _auto_annotation(_re.definebits, a[i], "datatype", location);
|
||||
}
|
||||
}
|
||||
a = _rebuild(a);
|
||||
for (i = 0; i < a.length; i++) {
|
||||
if (typeof(a[i]) === "string") {
|
||||
a[i] = new Annotation(a[i], location, "offset");
|
||||
}
|
||||
}
|
||||
return _rebuild(a);
|
||||
},
|
||||
/* user defined */
|
||||
comment: function(value, location) {
|
||||
return new Annotation(value, location, "comment");
|
||||
},
|
||||
constvar: function(value, location) {
|
||||
return new Annotation(value, location, "constant_variable");
|
||||
},
|
||||
datatype: function(value, location) {
|
||||
return new Annotation(value, location, "datatype");
|
||||
},
|
||||
funcname: function(value, location) {
|
||||
return new Annotation(value, location, "function_name");
|
||||
},
|
||||
funcparam: function(value, location) {
|
||||
return new Annotation(value, location, "function_parameter");
|
||||
},
|
||||
keyword: function(value, location) {
|
||||
return new Annotation(value, location, "keyword");
|
||||
},
|
||||
localvar: function(value, location) {
|
||||
return new Annotation(value, location, "local_variable");
|
||||
},
|
||||
offset: function(value, location) {
|
||||
return new Annotation(value, location, "offset");
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,403 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019-2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
const Base = require('libdec/core/base');
|
||||
const Variable = require('libdec/core/variable');
|
||||
//const Extra = require('libdec/core/extra');
|
||||
const _REG = {
|
||||
X: 'x',
|
||||
Y: 'y',
|
||||
stackptr: 'SP',
|
||||
accumulator: 'acc',
|
||||
carry: 'C',
|
||||
overflow: 'V',
|
||||
decimal: 'D',
|
||||
interrupt: 'I',
|
||||
zero: 'Z',
|
||||
};
|
||||
|
||||
function _arg(instr, idx) {
|
||||
var o = instr.parsed.opd;
|
||||
if (idx > 0) {
|
||||
return o[idx];
|
||||
}
|
||||
o = o[0];
|
||||
if (o.immediate) {
|
||||
return o.token;
|
||||
}
|
||||
return Variable.pointer(o.token, 16, false);
|
||||
}
|
||||
|
||||
function _math(op, instr, bits) {
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
if (!srcB) {
|
||||
return op(_REG.accumulator, _REG.accumulator, srcA, bits);
|
||||
}
|
||||
return Base.composed([
|
||||
op(_REG.accumulator, _REG.accumulator, srcA, bits),
|
||||
Base.add(_REG.accumulator, _REG.accumulator, srcB),
|
||||
]);
|
||||
}
|
||||
|
||||
function _math_rotate(op, instr, bits) {
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
if (!srcB) {
|
||||
// destination, source_a, source_b, bits
|
||||
return op(_REG.accumulator, srcA, bits, 16);
|
||||
}
|
||||
return Base.composed([
|
||||
op(_REG.accumulator, srcA, bits, 16),
|
||||
Base.add(_REG.accumulator, _REG.accumulator, srcB),
|
||||
]);
|
||||
}
|
||||
|
||||
function _load(instr, register, bits) {
|
||||
bits = bits || 16;
|
||||
register = register || _REG.accumulator;
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
var ops = [];
|
||||
if (typeof srcA == "string") {
|
||||
// pointer, register, bits, is_signed
|
||||
ops.push(Base.read_memory(register, register, srcA, bits));
|
||||
} else {
|
||||
ops.push(Base.assign(register, srcA));
|
||||
}
|
||||
if (!srcB) {
|
||||
return ops[0];
|
||||
}
|
||||
ops.push(Base.add(register, register, srcB));
|
||||
return Base.composed(ops);
|
||||
}
|
||||
|
||||
function _store(instr, register, bits) {
|
||||
bits = bits || 16;
|
||||
register = register || _REG.accumulator;
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
var ops = [];
|
||||
if (typeof srcA == "string") {
|
||||
// pointer, register, bits, is_signed
|
||||
ops.push(Base.write_memory(register, register, srcA, bits));
|
||||
} else {
|
||||
ops.push(Base.assign(srcA, register));
|
||||
}
|
||||
if (!srcB) {
|
||||
return ops[0];
|
||||
}
|
||||
ops.push(Base.add(register, register, srcB));
|
||||
return Base.composed(ops);
|
||||
}
|
||||
|
||||
function _nop(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
return {
|
||||
instructions: {
|
||||
adc: function(instr) {
|
||||
return _math(Base.add, instr);
|
||||
},
|
||||
and: function(instr) {
|
||||
return _math(Base.and, instr);
|
||||
},
|
||||
asl: function(instr) {
|
||||
return _math(Base.shift_left, instr, 1);
|
||||
},
|
||||
bit: function(instr, context) {
|
||||
var p = _arg(instr, 0);
|
||||
context.cond.a = '(' + _REG.accumulator + ' & ' + p + ')';
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
bcc: function(instr, context) {
|
||||
instr.conditional(_REG.carry, 0, 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
bcs: function(instr, context) {
|
||||
instr.conditional(_REG.carry, 0, 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
beq: function(instr, context) {
|
||||
instr.conditional(context.cond.a, context.cond.b, 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
bmi: function(instr, context) {
|
||||
instr.conditional(context.cond.a, context.cond.b, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
bne: function(instr, context) {
|
||||
instr.conditional(context.cond.a, context.cond.b, 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
bpl: function(instr, context) {
|
||||
instr.conditional(context.cond.a, context.cond.b, 'GT');
|
||||
return Base.nop();
|
||||
},
|
||||
brk: function(instr, context) {
|
||||
Global.context.addMacro('#define INTERRUPT() __asm(brk)');
|
||||
return Base.call(Variable.macro('INTERRUPT'), []);
|
||||
},
|
||||
bvc: function(instr, context) {
|
||||
instr.conditional(_REG.overflow, 0, 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
bvs: function(instr, context) {
|
||||
instr.conditional(_REG.overflow, 0, 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
clc: function(instr, context) {
|
||||
return Base.assign(_REG.carry, '0');
|
||||
},
|
||||
cld: function(instr, context) {
|
||||
return Base.assign(_REG.decimal, '0');
|
||||
},
|
||||
cli: function(instr, context) {
|
||||
return Base.assign(_REG.interrupt, '0');
|
||||
},
|
||||
clv: function(instr, context) {
|
||||
return Base.assign(_REG.overflow, '0');
|
||||
},
|
||||
cmp: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _REG.accumulator;
|
||||
var srcA = _arg(instr, 0);
|
||||
var srcB = _arg(instr, 1);
|
||||
if (!srcB) {
|
||||
context.cond.b = srcA;
|
||||
return Base.nop();
|
||||
}
|
||||
var v = Variable.uniqueName();
|
||||
context.cond.b = v;
|
||||
return Base.add(v, srcA, srcB);
|
||||
},
|
||||
cpx: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = _arg(instr, 0);
|
||||
return Base.nop();
|
||||
},
|
||||
cpy: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = _arg(instr, 0);
|
||||
return Base.nop();
|
||||
},
|
||||
dec: function(instr, context) {
|
||||
var p = _arg(instr, 0);
|
||||
context.cond.a = p;
|
||||
context.cond.b = '0';
|
||||
return Base.subtract(p, p, 1);
|
||||
},
|
||||
dex: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
return Base.subtract(_REG.X, _REG.X, 1);
|
||||
},
|
||||
dey: function(instr, context) {
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = '0';
|
||||
return Base.subtract(_REG.Y, _REG.Y, 1);
|
||||
},
|
||||
eor: function(instr, context) {
|
||||
return _math(Base.xor, instr);
|
||||
},
|
||||
inc: function(instr, context) {
|
||||
var p = _arg(instr, 0);
|
||||
context.cond.a = p;
|
||||
context.cond.b = '0';
|
||||
return Base.add(p, p, 1);
|
||||
},
|
||||
inx: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
return Base.add(_REG.X, _REG.X, 1);
|
||||
},
|
||||
iny: function(instr, context) {
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = '0';
|
||||
return Base.add(_REG.Y, _REG.Y, 1);
|
||||
},
|
||||
jmp: _nop,
|
||||
jsr: function(instr) {
|
||||
var p = instr.parsed.opd[0].token;
|
||||
if (p.startsWith('0x')) {
|
||||
p = Variable.functionPointer(p);
|
||||
}
|
||||
return Base.call(p);
|
||||
},
|
||||
lda: function(instr, context) {
|
||||
context.cond.a = _REG.accumulator;
|
||||
context.cond.b = '0';
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(_REG.accumulator, instr.parsed.opd[0].token.replace(/^sym\./, ''));
|
||||
}
|
||||
return _load(instr, _REG.accumulator, 16);
|
||||
},
|
||||
ldx: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(_REG.X, instr.parsed.opd[0].token.replace(/^sym\./, ''));
|
||||
}
|
||||
return _load(instr, _REG.X, 16);
|
||||
},
|
||||
ldy: function(instr, context) {
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = '0';
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(_REG.Y, instr.parsed.opd[0].token.replace(/^sym\./, ''));
|
||||
}
|
||||
return _load(instr, _REG.Y, 16);
|
||||
},
|
||||
lsr: function(instr) {
|
||||
return _math(Base.shift_right, instr, 1);
|
||||
},
|
||||
ora: function(instr, context) {
|
||||
return _math(Base.xor, instr);
|
||||
},
|
||||
pha: _nop,
|
||||
php: _nop,
|
||||
pla: _nop,
|
||||
plp: _nop,
|
||||
rol: function(instr) {
|
||||
return _math_rotate(Base.rotate_left, instr, 1);
|
||||
},
|
||||
ror: function(instr) {
|
||||
return _math_rotate(Base.rotate_right, instr, 1);
|
||||
},
|
||||
rti: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
rts: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
sbc: function(instr) {
|
||||
return _math(Base.subtract, instr);
|
||||
},
|
||||
sec: function(instr, context) {
|
||||
return Base.assign(_REG.carry, 1);
|
||||
},
|
||||
sed: function(instr, context) {
|
||||
return Base.assign(_REG.decimal, 1);
|
||||
},
|
||||
sei: function(instr, context) {
|
||||
return Base.assign(_REG.interrupt, 1);
|
||||
},
|
||||
sta: function(instr) {
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(instr.parsed.opd[0].token.replace(/^sym\./, ''), _REG.accumulator);
|
||||
}
|
||||
return _store(instr, _REG.accumulator, 16);
|
||||
},
|
||||
stx: function(instr) {
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(instr.parsed.opd[0].token.replace(/^sym\./, ''), _REG.X);
|
||||
}
|
||||
return _store(instr, _REG.X, 16);
|
||||
},
|
||||
sty: function(instr) {
|
||||
var o = instr.parsed.opd[0];
|
||||
if (o.token.startsWith('sym.') || o.immediate) {
|
||||
return Base.assign(instr.parsed.opd[0].token.replace(/^sym\./, ''), _REG.Y);
|
||||
}
|
||||
return _store(instr, _REG.Y, 16);
|
||||
},
|
||||
tax: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.X, _REG.accumulator);
|
||||
},
|
||||
tay: function(instr, context) {
|
||||
context.cond.a = _REG.Y;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.Y, _REG.accumulator);
|
||||
},
|
||||
tsx: function(instr, context) {
|
||||
context.cond.a = _REG.X;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.X, _REG.stackptr);
|
||||
},
|
||||
txs: function(instr, context) {
|
||||
return Base.assign(_REG.stackptr, _REG.X);
|
||||
},
|
||||
txa: function(instr, context) {
|
||||
context.cond.a = _REG.accumulator;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.accumulator, _REG.X);
|
||||
},
|
||||
tya: function(instr, context) {
|
||||
context.cond.a = _REG.accumulator;
|
||||
context.cond.b = '0';
|
||||
return Base.assign(_REG.accumulator, _REG.Y);
|
||||
},
|
||||
nop: _nop,
|
||||
invalid: _nop
|
||||
},
|
||||
parse: function(assembly) {
|
||||
var tokens = assembly.trim().match(/^([\w]+) ?(#)?(\()?((0x[\da-fA-F]+)|([a-zA-Z0-9._]+))?(\))?(,([\w]+))?\)?$/);
|
||||
var opds = [];
|
||||
if (tokens[5] || tokens[6]) {
|
||||
var token = tokens[5] || tokens[6];
|
||||
if (tokens[3] == '(' && tokens[7] == ')' && tokens[9]) {
|
||||
token += ' + ' + tokens[9];
|
||||
}
|
||||
opds.push({
|
||||
immediate: tokens[2] == '#',
|
||||
pointer: tokens[3] == '(',
|
||||
token: token,
|
||||
});
|
||||
}
|
||||
if (tokens[9] && tokens[3] == '(' && tokens[7] != ')') {
|
||||
opds.push(tokens[9]);
|
||||
}
|
||||
return {
|
||||
mnem: tokens[1],
|
||||
opd: opds
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: _REG.accumulator,
|
||||
b: '?',
|
||||
}
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions, context) {},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,293 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
const Base = require('libdec/core/base');
|
||||
const Variable = require('libdec/core/variable');
|
||||
|
||||
const EXTRAM = "eram + ";
|
||||
const INDRAM = "iram + ";
|
||||
|
||||
function resolve_opd(instr, index) {
|
||||
var a = instr.parsed.opd[index].replace(/\+/, ' + ');
|
||||
if (a.indexOf('#') == 0) {
|
||||
return a.substr(1, a.length);
|
||||
} else if (a.match(/^\d+$|^0x[a-fA-F\d]+$/)) {
|
||||
return Variable.pointer(INDRAM + a, 8, false);
|
||||
} else if (a.indexOf('@') == 0) {
|
||||
return Variable.pointer(EXTRAM + a.substr(1, a.length), 8, false);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function previous(instr, instructions) {
|
||||
return instructions[instructions.indexOf(instr) - 1];
|
||||
}
|
||||
|
||||
function is_mnem(instr, mnem, startswith) {
|
||||
if (startswith) {
|
||||
return instr && instr.parsed.mnem.indexOf(mnem) == 0;
|
||||
}
|
||||
return instr && instr.parsed.mnem == mnem;
|
||||
}
|
||||
|
||||
function is_last(instr, instructions) {
|
||||
return instructions.indexOf(instr) == (instructions.length - 1);
|
||||
}
|
||||
|
||||
function is_membit(instr, index) {
|
||||
return instr.parsed.opd[index].indexOf('.') > 0;
|
||||
}
|
||||
|
||||
function write_membit(instr, index, b) {
|
||||
var a = instr.parsed.opd[index].split('.');
|
||||
var p = Variable.pointer(a[0], 8, false);
|
||||
if (b == '1') {
|
||||
b = '0x' + (1 << parseInt(a[1])).toString(16);
|
||||
} else if (b != '0') {
|
||||
b = '(' + b + ' & 1)' + (parseInt(a[1]) > 0 ? ' << ' + a[1] : '');
|
||||
} else {
|
||||
var bit = parseInt(a[1]) < 1 ? '~1' : '~(1 << ' + a[1] + ')';
|
||||
return Base.and(p, p, bit);
|
||||
}
|
||||
return Base.or(p, p, b);
|
||||
}
|
||||
|
||||
function read_membit(instr, index, b) {
|
||||
var a = instr.parsed.opd[index].split('.');
|
||||
var p = Variable.pointer(a[0], 8, false);
|
||||
if (parseInt(a[1]) < 1) {
|
||||
return Base.and(b, p, '1');
|
||||
}
|
||||
return Base.assign(b, '(' + p.toString() + ' >> ' + a[1] + ') & 1');
|
||||
}
|
||||
|
||||
function _mov(instr, context, instructions) {
|
||||
if (is_membit(instr, 0)) {
|
||||
return write_membit(instr, 0, resolve_opd(instr, 1));
|
||||
} else if (is_membit(instr, 1)) {
|
||||
return read_membit(instr, 1, resolve_opd(instr, 0));
|
||||
}
|
||||
var a = resolve_opd(instr, 0);
|
||||
var b = resolve_opd(instr, 1);
|
||||
return Base.assign(a, b);
|
||||
}
|
||||
|
||||
function _call(instr, context, instructions) {
|
||||
var args = [];
|
||||
var prev = previous(instr, instructions);
|
||||
if (prev && prev.parsed.opd[0] == 'a') {
|
||||
args.push(prev.parsed.opd[0]);
|
||||
}
|
||||
return Base.call(instr.parsed.opd[0], args);
|
||||
}
|
||||
|
||||
function _jump(instr, context, instructions) {
|
||||
if (is_last(instr, instructions) &&
|
||||
(instr.jump.gt(instr.location) || instr.jump.lt(instructions[0].location))) {
|
||||
return _call(instr, context, instructions);
|
||||
}
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
function _math(instr, context, op, carry) {
|
||||
if (carry) {
|
||||
return op('a', op(resolve_opd(instr, 0), resolve_opd(instr, 1)), 'carry');
|
||||
}
|
||||
return op('a', resolve_opd(instr, 0), resolve_opd(instr, 1));
|
||||
}
|
||||
|
||||
function _nop(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
return {
|
||||
instructions: {
|
||||
acall: _call,
|
||||
add: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.add);
|
||||
},
|
||||
addc: function(instr, context, instructions) {
|
||||
instr.comments.push("add with carry");
|
||||
return _math(instr, context, Base.add, true);
|
||||
},
|
||||
ajmp: _jump,
|
||||
anl: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.and);
|
||||
},
|
||||
cjne: function(instr, context, instructions) {
|
||||
instr.conditional(resolve_opd(instr, 0), resolve_opd(instr, 1), 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
clr: function(instr, context, instructions) {
|
||||
if (is_membit(instr, 0)) {
|
||||
return write_membit(instr, 0, '0');
|
||||
}
|
||||
return Base.assign(resolve_opd(instr, 0), '0');
|
||||
},
|
||||
cpl: function(instr, context, instructions) {
|
||||
return Base.not(instr.parsed.opd[0], instr.parsed.opd[0]);
|
||||
},
|
||||
dec: function(instr, context, instructions) {
|
||||
return Base.decrease(resolve_opd(instr, 0), '1');
|
||||
},
|
||||
div: function(instr, context, instructions) {
|
||||
return Base.divide('a', 'a', 'b');
|
||||
},
|
||||
djnz: function(instr, context, instructions) {
|
||||
var a = resolve_opd(instr, 0);
|
||||
instr.conditional(a, '0', 'NE');
|
||||
return Base.decrease(a, '1');
|
||||
},
|
||||
inc: function(instr, context, instructions) {
|
||||
return Base.increase(resolve_opd(instr, 0), '1');
|
||||
},
|
||||
jb: function(instr, context, instructions) {
|
||||
instr.conditional('b', '0', 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
jbc: function(instr, context, instructions) {
|
||||
instr.conditional('b', '0 && c', 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
jc: function(instr, context) {
|
||||
instr.conditional('c', '0', 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
jmp: _jump,
|
||||
jnb: function(instr, context, instructions) {
|
||||
instr.conditional('b', '0', 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
jnc: function(instr, context, instructions) {
|
||||
instr.conditional('c', '0', 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
jnz: function(instr, context, instructions) {
|
||||
var p = previous(instr, instructions);
|
||||
instr.conditional(is_mnem(p, 'mov', true) ? resolve_opd(p, 1) : 'a', '0', 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
jz: function(instr, context, instructions) {
|
||||
var p = previous(instr, instructions);
|
||||
instr.conditional(is_mnem(p, 'mov', true) ? resolve_opd(p, 1) : 'a', '0', 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
lcall: _call,
|
||||
ljmp: _jump,
|
||||
mov: _mov,
|
||||
movc: _mov,
|
||||
movx: _mov,
|
||||
mul: function(instr, context, instructions) {
|
||||
return Base.multiply('a', 'a', 'b');
|
||||
},
|
||||
orl: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.or);
|
||||
},
|
||||
pop: function(instr) {
|
||||
return Base.assign(resolve_opd(instr, 0), Base.call('pop', []));
|
||||
},
|
||||
push: function(instr) {
|
||||
return Base.call('push', [resolve_opd(instr, 0)]);
|
||||
},
|
||||
ret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
reti: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
rl: function(instr) {
|
||||
return Base.rotate_left('a', 'a', '1', 8);
|
||||
},
|
||||
rlc: function(instr) {
|
||||
instr.comments.push("rotate with carry");
|
||||
return Base.rotate_left('a', 'a', '1', 8);
|
||||
},
|
||||
rr: function(instr) {
|
||||
return Base.rotate_right('a', 'a', '1', 8);
|
||||
},
|
||||
rrc: function(instr) {
|
||||
instr.comments.push("rotate with carry");
|
||||
return Base.rotate_right('a', 'a', '1', 8);
|
||||
},
|
||||
setb: function(instr, context, instructions) {
|
||||
if (is_membit(instr, 0)) {
|
||||
return write_membit(instr, 0, '1');
|
||||
}
|
||||
return Base.assign(instr.parsed.opd[0], 1);
|
||||
},
|
||||
sjmp: _jump,
|
||||
subb: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.subtract);
|
||||
},
|
||||
swap: function(instr, context, instructions) {
|
||||
return Base.swap_endian(resolve_opd(instr, 1), resolve_opd(instr, 0), 8);
|
||||
},
|
||||
xch: function(instr, context, instructions) {
|
||||
return Base.swap(resolve_opd(instr, 0), resolve_opd(instr, 1), 8);
|
||||
},
|
||||
xchd: function(instr, context, instructions) {
|
||||
var a = resolve_opd(instr, 0);
|
||||
var b = resolve_opd(instr, 1);
|
||||
var tmp = Variable.uniqueName('tmp');
|
||||
var ops = [
|
||||
Base.and(tmp, a, '0xF'),
|
||||
Base.and(a, a, '0xF0'),
|
||||
Base.or(a, b, '0xF0'),
|
||||
Base.and(b, b, '0xF0'),
|
||||
Base.or(b, b, tmp),
|
||||
];
|
||||
context.local.push(tmp);
|
||||
return Base.composed(ops);
|
||||
},
|
||||
xrl: function(instr, context, instructions) {
|
||||
return _math(instr, context, Base.xor);
|
||||
},
|
||||
nop: _nop,
|
||||
reserved: _nop,
|
||||
invalid: _nop
|
||||
},
|
||||
parse: function(assembly) {
|
||||
assembly = assembly.trim().replace(/,/g, '');
|
||||
var tokens = assembly.split(' ');
|
||||
return {
|
||||
mnem: tokens[0],
|
||||
opd: tokens.splice(1)
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
local: []
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions, context) {},
|
||||
localvars: function(context) {
|
||||
return context.local.map(function(x) {
|
||||
return 'uint8_t ' + x;
|
||||
});
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
});
|
||||
-1878
File diff suppressed because it is too large.
Load diff
@@ -1,690 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
|
||||
var Variable = require('libdec/core/variable');
|
||||
var Base = require('libdec/core/base');
|
||||
|
||||
const AVR_MEM_BITS = 16;
|
||||
const AVR_X_MACRONAME = 'MEM_X';
|
||||
const AVR_Y_MACRONAME = 'MEM_Y';
|
||||
const AVR_Z_MACRONAME = 'MEM_Z';
|
||||
const AVR_X_MACRO = '#define ' + AVR_X_MACRONAME + ' ((uint16_t*)((r27 << 8) | r26))';
|
||||
const AVR_Y_MACRO = '#define ' + AVR_Y_MACRONAME + ' ((uint16_t*)((r29 << 8) | r28))';
|
||||
const AVR_Z_MACRO = '#define ' + AVR_Z_MACRONAME + ' ((uint16_t*)((r31 << 8) | r30))';
|
||||
const AVR_MEMORY = {
|
||||
'x': {
|
||||
name: AVR_X_MACRONAME,
|
||||
macro: AVR_X_MACRO,
|
||||
high: 'r27',
|
||||
low: 'r26'
|
||||
},
|
||||
'y': {
|
||||
name: AVR_Y_MACRONAME,
|
||||
macro: AVR_Y_MACRO,
|
||||
high: 'r29',
|
||||
low: 'r28'
|
||||
},
|
||||
'z': {
|
||||
name: AVR_Z_MACRONAME,
|
||||
macro: AVR_Z_MACRO,
|
||||
high: 'r31',
|
||||
low: 'r30'
|
||||
}
|
||||
};
|
||||
var AVR_CTR = 0;
|
||||
|
||||
var _to_16bit = function(high, low) {
|
||||
if ((high == '0x00' || high == '0') && (low == '0x00' || low == '0')) {
|
||||
return '0';
|
||||
} else if (low == '0x00' || low == '0') {
|
||||
return '(' + high + ' << 8)';
|
||||
} else if (high == '0x00' || high == '0') {
|
||||
return low;
|
||||
}
|
||||
return '(' + low + ' | (' + high + ' << 8))';
|
||||
};
|
||||
|
||||
var _common_math = function(instr, op) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed.opd;
|
||||
return op(e[0], e[0], e[1]);
|
||||
};
|
||||
|
||||
var _compare = function(a, b, context) {
|
||||
context.cond.a = a;
|
||||
context.cond.b = b;
|
||||
};
|
||||
|
||||
var _compare_values = function(a1, b1, a2, b2, context) {
|
||||
context.cond.a = _to_16bit(a2, a1);
|
||||
context.cond.b = _to_16bit(b2, b1);
|
||||
};
|
||||
|
||||
var _compare_bytes = function(a, b, context) {
|
||||
var a1 = context.cond.a;
|
||||
var b1 = context.cond.b;
|
||||
_compare_values(a1, b1, a, b, context);
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, type) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
if (context.cond.instr) {
|
||||
context.cond.instr.valid = false;
|
||||
}
|
||||
};
|
||||
|
||||
var _next_register = function(reg) {
|
||||
var num = parseInt(reg.substr(1, reg.length)) + 1;
|
||||
return 'r' + num;
|
||||
};
|
||||
|
||||
var _returns_r0 = function(instr, context, inverted) {
|
||||
if (inverted && instr.parsed.opd[1] == 'r0') {
|
||||
context.returns = 'r0';
|
||||
} else if (!inverted && instr.parsed.opd[0] == 'r0') {
|
||||
context.returns = 'r0';
|
||||
}
|
||||
};
|
||||
|
||||
var _conditional_next = function(instr, context, instructions, type) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
var next = instructions[instructions.indexOf(instr) + 1];
|
||||
if (next) {
|
||||
instr.jump = next.location;
|
||||
}
|
||||
if (context.cond.instr) {
|
||||
context.cond.instr.valid = false;
|
||||
}
|
||||
};
|
||||
|
||||
var _load16 = function(instr, context) {
|
||||
var v, m, op;
|
||||
_returns_r0(instr, context);
|
||||
var ptr = instr.parsed.opd[1];
|
||||
if (ptr.indexOf('-') >= 0) {
|
||||
ptr = ptr.replace('-', '');
|
||||
v = Variable.uniqueName('local');
|
||||
//pointer, register, bits, is_signed
|
||||
m = [
|
||||
Base.read_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[0], 8, false),
|
||||
Base.assign('uint8_t ' + v, AVR_MEMORY[ptr].low),
|
||||
Base.subtract(AVR_MEMORY[ptr].low, AVR_MEMORY[ptr].low, 1),
|
||||
Base.conditional_math(AVR_MEMORY[ptr].high, AVR_MEMORY[ptr].low, v, 'LT', AVR_MEMORY[ptr].high, 1, AVR_MEMORY[ptr].high, '-')
|
||||
];
|
||||
op = Base.composed(m);
|
||||
Global.context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
} else if (ptr.indexOf('+') >= 0) {
|
||||
ptr = ptr.replace('+', '');
|
||||
v = Variable.uniqueName('local');
|
||||
//pointer, register, bits, is_signed
|
||||
m = [
|
||||
Base.read_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[0], 8, false),
|
||||
Base.assign('uint8_t ' + v, AVR_MEMORY[ptr].low),
|
||||
Base.add(AVR_MEMORY[ptr].low, AVR_MEMORY[ptr].low, 1),
|
||||
Base.conditional_math(AVR_MEMORY[ptr].high, AVR_MEMORY[ptr].low, v, 'LT', AVR_MEMORY[ptr].high, 1, AVR_MEMORY[ptr].high, '+')
|
||||
];
|
||||
op = Base.composed(m);
|
||||
Global.context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
}
|
||||
//pointer, register, bits, is_signed
|
||||
op = Base.read_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[0], 8, false);
|
||||
Global.context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
};
|
||||
|
||||
var _store16 = function(instr, context) {
|
||||
var v, m, op;
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
var ptr = instr.parsed.opd[0];
|
||||
if (ptr.indexOf('-') >= 0) {
|
||||
ptr = ptr.replace('-', '');
|
||||
v = Variable.uniqueName('local');
|
||||
//pointer, register, bits, is_signed
|
||||
m = [
|
||||
Base.write_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[1], 8, false),
|
||||
Base.assign('uint8_t ' + v, AVR_MEMORY[ptr].low),
|
||||
Base.subtract(AVR_MEMORY[ptr].low, AVR_MEMORY[ptr].low, 1),
|
||||
Base.conditional_math(AVR_MEMORY[ptr].high, AVR_MEMORY[ptr].low, v, 'LT', AVR_MEMORY[ptr].high, 1, AVR_MEMORY[ptr].high, '-')
|
||||
];
|
||||
op = Base.composed(m);
|
||||
Global.context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
} else if (ptr.indexOf('+') >= 0) {
|
||||
ptr = ptr.replace('+', '');
|
||||
v = Variable.uniqueName('local');
|
||||
//pointer, register, bits, is_signed
|
||||
m = [
|
||||
Base.write_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[1], 8, false),
|
||||
Base.assign('uint8_t ' + v, AVR_MEMORY[ptr].low),
|
||||
Base.add(AVR_MEMORY[ptr].low, AVR_MEMORY[ptr].low, 1),
|
||||
Base.conditional_math(AVR_MEMORY[ptr].high, AVR_MEMORY[ptr].low, v, 'LT', AVR_MEMORY[ptr].high, 1, AVR_MEMORY[ptr].high, '+')
|
||||
];
|
||||
op = Base.composed(m);
|
||||
Global.context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
}
|
||||
//pointer, register, bits, is_signed
|
||||
op = Base.write_memory(AVR_MEMORY[ptr].name, instr.parsed.opd[1], 8, false);
|
||||
Global.context.addMacro(AVR_MEMORY[ptr].macro);
|
||||
return op;
|
||||
};
|
||||
|
||||
return {
|
||||
instructions: {
|
||||
adc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.add);
|
||||
},
|
||||
add: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.add);
|
||||
},
|
||||
adiw: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
var b = instr.parsed.opd[1];
|
||||
var a1 = instr.parsed.opd[0];
|
||||
var a2 = _next_register(instr.parsed.opd[0]);
|
||||
_compare_values(a1, b, a2, b, context);
|
||||
context.cond.instr = instr;
|
||||
if (b == '0x00' || b == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var op1 = null;
|
||||
var op2 = null;
|
||||
if (b == '0x01' || b == '1') {
|
||||
op1 = Base.increase(a1, '1');
|
||||
op2 = Base.increase(a2, '1');
|
||||
} else {
|
||||
op1 = Base.add(a1, a1, b);
|
||||
op2 = Base.add(a2, a2, b);
|
||||
}
|
||||
return Base.composed([op1, op2]);
|
||||
},
|
||||
and: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.and);
|
||||
},
|
||||
andi: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.and);
|
||||
},
|
||||
asr: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.shift_right(instr.parsed.opd[0], instr.parsed.opd[0], '1');
|
||||
},
|
||||
brbc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var old_b = context.cond.b;
|
||||
context.cond.b = '0';
|
||||
_conditional(instr, context, 'NE');
|
||||
context.cond.b = old_b;
|
||||
return Base.nop();
|
||||
},
|
||||
brbs: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var old_b = context.cond.b;
|
||||
context.cond.b = '(1 << ' + instr.parsed.opd[0] + ')';
|
||||
_conditional(instr, context, 'NE');
|
||||
context.cond.b = old_b;
|
||||
return Base.nop();
|
||||
},
|
||||
brcc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'GE');
|
||||
return Base.nop();
|
||||
},
|
||||
brcs: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
breq: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'EQ');
|
||||
return Base.nop();
|
||||
},
|
||||
brlo: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
brlt: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
brmi: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LT');
|
||||
return Base.nop();
|
||||
},
|
||||
brne: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'NE');
|
||||
return Base.nop();
|
||||
},
|
||||
brpl: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'GT');
|
||||
return Base.nop();
|
||||
},
|
||||
brsh: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_conditional(instr, context, 'LE');
|
||||
return Base.nop();
|
||||
},
|
||||
bset: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.macro('SREG_SET_BIT (' + instr.parsed.opd[0] + ')', '#define SREG_SET_BIT(x) __asm(bset (x))');
|
||||
},
|
||||
call: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
//name, args, is_pointer, returns, bits
|
||||
if (instr.parsed.opd[0].indexOf('0x') == 0) {
|
||||
return Base.call(Variable.functionPointer(instr.parsed.opd[0], 16), []);
|
||||
}
|
||||
return Base.call(instr.parsed.opd[0], []);
|
||||
},
|
||||
cli: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.macro('DISABLE_INTERRUPTS', '#define DISABLE_INTERRUPTS __asm(cli)');
|
||||
},
|
||||
clr: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.assign(instr.parsed.opd[0], '0');
|
||||
},
|
||||
clt: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.macro('CLEAR_TRANSFER_FLAG', '#define CLEAR_TRANSFER_FLAG __asm(clt)');
|
||||
},
|
||||
com: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.subtract(instr.parsed.opd[0], '0xff', instr.parsed.opd[0]);
|
||||
},
|
||||
cp: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return Base.nop();
|
||||
},
|
||||
cpc: function(instr, context, instructions) {
|
||||
instr.setBadJump();
|
||||
var p = instructions.indexOf(instr);
|
||||
if (instructions[p - 1] && instructions[p - 1].parsed.mnem.indexOf('cp') == 0) {
|
||||
_compare_bytes(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
} else {
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
}
|
||||
context.cond.instr = instr;
|
||||
return Base.nop();
|
||||
},
|
||||
cpi: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return Base.nop();
|
||||
},
|
||||
dec: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
_compare('--' + instr.parsed.opd[0], '0', context);
|
||||
context.cond.instr = instr;
|
||||
return Base.decrease(instr.parsed.opd[0], '1');
|
||||
},
|
||||
eor: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.xor);
|
||||
},
|
||||
icall: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
instr.setBadJump();
|
||||
//name, args, is_pointer, returns, bits
|
||||
var op = Base.call(Variable.functionPointer(AVR_MEMORY.z.name), []);
|
||||
Global.context.addMacro(AVR_MEMORY.z.macro);
|
||||
return op;
|
||||
},
|
||||
ijmp: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
instr.setBadJump();
|
||||
var op = Base.goto(AVR_MEMORY.z.name);
|
||||
Global.context.addMacro(AVR_MEMORY.z.macro);
|
||||
return op;
|
||||
},
|
||||
in: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var e = instr.parsed.opd;
|
||||
return Base.macro('READ_FROM_IO (' + e[1] + ', ' + e[0] + ')', '#define READ_FROM_IO(x,y) __asm(in (y), (x))');
|
||||
},
|
||||
inc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare('++' + instr.parsed.opd[0], '0', context);
|
||||
context.cond.instr = instr;
|
||||
return Base.increase(instr.parsed.opd[0], '1');
|
||||
},
|
||||
iret: function(instr) {
|
||||
return Base.macro('RETURN_FROM_INTERRUPT', '#define RETURN_FROM_INTERRUPT __asm(iret)');
|
||||
},
|
||||
jmp: function(instr, context) {
|
||||
return Base.nop();
|
||||
},
|
||||
ld: _load16,
|
||||
ldd: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var ptr = instr.parsed.opd[1].match(/([xyz]|[+-0-9]+)/g);
|
||||
var offset = ptr[1].replace(/(-)/, ' - ').replace(/(\+)/, ' + ');
|
||||
//pointer, register, bits, is_signed
|
||||
var op = Base.read_memory(AVR_MEMORY[ptr[0]].name + offset, instr.parsed.opd[0], 8, false);
|
||||
Global.context.addMacro(AVR_MEMORY[ptr[0]].macro);
|
||||
return op;
|
||||
},
|
||||
ldi: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.assign(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
lds: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(instr.parsed.opd[1], instr.parsed.opd[0], 8, false);
|
||||
},
|
||||
lpm: _load16,
|
||||
lsl: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.shift_left(instr.parsed.opd[0], instr.parsed.opd[0], '1');
|
||||
},
|
||||
lsr: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.shift_right(instr.parsed.opd[0], instr.parsed.opd[0], '1');
|
||||
},
|
||||
mov: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.assign(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
movw: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
var a1 = instr.parsed.opd[0];
|
||||
var a2 = _next_register(instr.parsed.opd[0]);
|
||||
var b1 = instr.parsed.opd[1];
|
||||
var b2 = _next_register(instr.parsed.opd[1]);
|
||||
var op1 = Base.assign(a1, b1);
|
||||
var op2 = Base.assign(a2, b2);
|
||||
return Base.composed([op1, op2]);
|
||||
},
|
||||
mul: function(instr, context) {
|
||||
context.returns = 'r0';
|
||||
var name = 'value' + (++AVR_CTR);
|
||||
var ops = [];
|
||||
ops.push(Base.multiply('uint16_t ' + name, instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.assign('r0', '(' + name + ' & 0xFF)'));
|
||||
ops.push(Base.assign('r1', '(' + name + ' >> 8)'));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
muls: function(instr, context) {
|
||||
context.returns = 'r0';
|
||||
var name = 'value' + (++AVR_CTR);
|
||||
var ops = [];
|
||||
ops.push(Base.multiply('int16_t ' + name, instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.assign('r0', '(' + name + ' & 0xFF)'));
|
||||
ops.push(Base.assign('r1', '(' + name + ' >> 8)'));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
mulsu: function(instr, context) {
|
||||
context.returns = 'r0';
|
||||
var name = 'value' + (++AVR_CTR);
|
||||
var ops = [];
|
||||
ops.push(Base.multiply('uint16_t ' + name, instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.assign('r0', '(' + name + ' & 0xFF)'));
|
||||
ops.push(Base.assign('r1', '(' + name + ' >> 8)'));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
neg: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.negate(instr.parsed.opd[0], instr.parsed.opd[0]);
|
||||
},
|
||||
nop: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
},
|
||||
not: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.not(instr.parsed.opd[0], instr.parsed.opd[0]);
|
||||
},
|
||||
or: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.or);
|
||||
},
|
||||
ori: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return _common_math(instr, Base.or);
|
||||
},
|
||||
out: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context, true);
|
||||
var e = instr.parsed.opd;
|
||||
return Base.macro('WRITE_TO_IO (' + e[0] + ', ' + e[1] + ')', '#define WRITE_TO_IO(x,y) __asm(in (x), (y))');
|
||||
},
|
||||
pop: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
if (instr.parsed.opd[0] == 'r0') {
|
||||
// if pops an r0, then the return value is void.
|
||||
context.returns = null;
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
push: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
},
|
||||
rcall: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.call(Variable.functionPointer(instr.parsed.opd[0], AVR_MEM_BITS), []);
|
||||
},
|
||||
ret: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
// returns r1:r0
|
||||
// but if pop r0 then return void.
|
||||
return Base.return(context.returns);
|
||||
},
|
||||
reti: function(instr) {
|
||||
return Base.macro('RETURN_FROM_INTERRUPT', '#define RETURN_FROM_INTERRUPT __asm(reti)');
|
||||
},
|
||||
rjmp: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
return Base.nop();
|
||||
},
|
||||
rol: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.rotate_left(instr.parsed.opd[0], instr.parsed.opd[0], '1', 8);
|
||||
},
|
||||
ror: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.rotate_right(instr.parsed.opd[0], instr.parsed.opd[0], '1', 8);
|
||||
},
|
||||
sbc: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.subtract);
|
||||
},
|
||||
sbci: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.subtract);
|
||||
},
|
||||
sbiw: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
var b = instr.parsed.opd[1];
|
||||
var a1 = instr.parsed.opd[0];
|
||||
var a2 = _next_register(instr.parsed.opd[0]);
|
||||
_compare_values(a1, b, a2, b, context);
|
||||
context.cond.instr = instr;
|
||||
if (b == '0x00' || b == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var op1 = null;
|
||||
var op2 = null;
|
||||
if (b == '0x01' || b == '1') {
|
||||
op1 = Base.decrease(a1, '1');
|
||||
op2 = Base.decrease(a2, '1');
|
||||
} else {
|
||||
op1 = Base.subtract(a1, a1, b);
|
||||
op2 = Base.subtract(a2, a2, b);
|
||||
}
|
||||
return Base.composed([op1, op2]);
|
||||
},
|
||||
sbr: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.or(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
sbrc: function(instr, context, instructions) {
|
||||
instr.setBadJump();
|
||||
var next = instructions[instructions.indexOf(instr) + 1];
|
||||
if (next) {
|
||||
_compare('(' + instr.parsed.opd[0] + ' & (1 << ' + instr.parsed.opd[1] + '))', '0', context);
|
||||
context.cond.instr = instr;
|
||||
_conditional_next(next, context, instructions, 'EQ');
|
||||
return Base.or(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
sbrs: function(instr, context, instructions) {
|
||||
instr.setBadJump();
|
||||
var next = instructions[instructions.indexOf(instr) + 1];
|
||||
if (next) {
|
||||
_compare('(' + instr.parsed.opd[0] + ' & (1 << ' + instr.parsed.opd[1] + '))', '1', context);
|
||||
context.cond.instr = instr;
|
||||
_conditional_next(next, context, instructions, 'EQ');
|
||||
return Base.or(instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
sei: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.macro('ENABLE_INTERRUPTS', '#define ENABLE_INTERRUPTS __asm(sei)');
|
||||
},
|
||||
ser: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.assign(instr.parsed.opd[0], '0xff');
|
||||
},
|
||||
set: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
return Base.macro('SET_TRANSFER_FLAG', '#define SET_TRANSFER_FLAG __asm(set)');
|
||||
},
|
||||
st: _store16,
|
||||
std: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
var ptr = instr.parsed.opd[0].match(/([xyz]|[+-0-9]+)/g);
|
||||
var offset = ptr[1].replace(/(-)/, ' - ').replace(/(\+)/, ' + ');
|
||||
//pointer, register, bits, is_signed
|
||||
var op = Base.write_memory(AVR_MEMORY[ptr[0]].name + offset, instr.parsed.opd[1], 8, false);
|
||||
Global.context.addMacro(AVR_MEMORY[ptr[0]].macro);
|
||||
return op;
|
||||
},
|
||||
sts: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_returns_r0(instr, context);
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(instr.parsed.opd[0], instr.parsed.opd[1], 8, false);
|
||||
},
|
||||
sub: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
return _common_math(instr, Base.subtract);
|
||||
},
|
||||
subi: function(instr, context) {
|
||||
_returns_r0(instr, context);
|
||||
_compare(instr.parsed.opd[0], instr.parsed.opd[1], context);
|
||||
context.cond.instr = instr;
|
||||
if (instr.parsed.opd[1] == '0x00' || instr.parsed.opd[1] == '0') {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
}
|
||||
return _common_math(instr, Base.subtract);
|
||||
},
|
||||
tst: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
_compare(instr.parsed.opd[0], '0', context);
|
||||
context.cond.instr = instr;
|
||||
},
|
||||
wdr: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
return Base.macro('CLEAR_WATCHDOG', '#define CLEAR_WATCHDOG __asm(wdr)');
|
||||
},
|
||||
invalid: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(asm) {
|
||||
asm = asm.toLowerCase();
|
||||
var ret = asm.replace(/\[|\]/g, ' ').replace(/,/g, ' ');
|
||||
ret = ret.replace(/\{|\}/g, ' ').replace(/\s+/g, ' ');
|
||||
ret = ret.trim().replace(/0x00/, '0').split(' ');
|
||||
|
||||
return {
|
||||
mnem: ret.shift(),
|
||||
opd: ret
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: null,
|
||||
b: null,
|
||||
instr: null
|
||||
},
|
||||
returns: null
|
||||
};
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return context.returns == null ? 'void' : 'uint8_t';
|
||||
}
|
||||
};
|
||||
});
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,444 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
|
||||
var Base = require('libdec/core/base');
|
||||
var Variable = require('libdec/core/variable');
|
||||
|
||||
var _to_size = function(value) {
|
||||
if (value == 'b') {
|
||||
return 8;
|
||||
} else if (value == 'w') {
|
||||
return 16;
|
||||
} else if (value == 'l') {
|
||||
return 32;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
var _as_pointer = function(e, where, signed, ispointer) {
|
||||
var arg = e.opd[where];
|
||||
if (arg.pointer) {
|
||||
if (arg.presub) {
|
||||
var m = e.bits / 8;
|
||||
if (m == 1) {
|
||||
arg = Variable.pointer('--' + arg.pointer, e.bits, signed);
|
||||
} else {
|
||||
arg = Variable.pointer(arg.pointer + ' -= ' + m, e.bits, signed);
|
||||
}
|
||||
} else {
|
||||
return Variable.pointer(arg.pointer, e.bits, signed);
|
||||
}
|
||||
} else if (ispointer && arg.register.indexOf('0x') == 0) {
|
||||
var tokens = arg.register.replace(/\.([bwl])/, ' $1').split(' ');
|
||||
arg = Variable.pointer(tokens[0], _to_size(tokens[1]), signed);
|
||||
} else {
|
||||
return arg.register;
|
||||
}
|
||||
return arg;
|
||||
};
|
||||
|
||||
var _is_register = function(name) {
|
||||
return name && name.match(/\b[acdsACDS][0-9ixIX]\b/);
|
||||
};
|
||||
|
||||
var _common_math = function(e, op) {
|
||||
var src = _as_pointer(e, 0, true);
|
||||
var dst = _as_pointer(e, 1, true);
|
||||
if (e.opd[1].postadd) {
|
||||
return Base.composed([op(dst, dst, src), Base.add(e.opd[1].register, e.opd[1].register, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op(dst, dst, src);
|
||||
};
|
||||
|
||||
var _move = function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var src = _as_pointer(e, 0, true, true);
|
||||
var dst = _as_pointer(e, 1, true, true);
|
||||
return Base.assign(dst, src);
|
||||
};
|
||||
|
||||
var _move_multiple = function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var memory, pointer, postadd, register, op;
|
||||
var e = instr.parsed;
|
||||
var incr = (e.bits / 8).toString();
|
||||
var ops = [];
|
||||
if (e.opd[0].pointer) {
|
||||
//movem.l -(a7), d2-d3/a2-a5
|
||||
memory = _as_pointer(e, 0, false);
|
||||
pointer = e.opd[0].register;
|
||||
postadd = e.opd[0].postadd;
|
||||
register = e.opd[1].register;
|
||||
op = Base.read_memory;
|
||||
} else {
|
||||
//movem.l d2-d3/a2-a5, -(a7)
|
||||
memory = _as_pointer(e, 1, false);
|
||||
pointer = e.opd[1].register;
|
||||
postadd = e.opd[1].postadd;
|
||||
register = e.opd[0].register;
|
||||
op = Base.write_memory;
|
||||
}
|
||||
var reg = register.split('/');
|
||||
for (var i = 0; i < reg.length; i++) {
|
||||
var prefix = reg[i].charAt(0);
|
||||
var tmp = reg[i].replace(/[a-zA-Z]/g, '').split('-');
|
||||
var start = tmp[0];
|
||||
var end = tmp[1] || tmp[0];
|
||||
for (var j = start; j <= end; j++) {
|
||||
ops.push(op(memory, prefix + j, e.bits, false));
|
||||
if (postadd) {
|
||||
ops.push(Base.add(pointer, pointer, incr));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Base.composed(ops);
|
||||
};
|
||||
|
||||
var _compare = function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _as_pointer(instr.parsed, 1, false);
|
||||
context.cond.b = _as_pointer(instr.parsed, 0, false);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, type, zero) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _bitmask = function(offset, size) {
|
||||
var mask = 0;
|
||||
for (var i = 0; i < size; i++) {
|
||||
mask <<= 1;
|
||||
mask |= 1;
|
||||
}
|
||||
return '0x' + ((mask << offset) >>> 0).toString(16);
|
||||
};
|
||||
|
||||
return {
|
||||
instructions: {
|
||||
add: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
addi: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
addq: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
and: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.and);
|
||||
},
|
||||
andi: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.and);
|
||||
},
|
||||
bftst: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var vals = instr.assembly.match(/(\w+)(?:{(\d+):(\d+)})/);
|
||||
var mask = _bitmask(vals[2], vals[3]);
|
||||
var test = Variable.local('(' + vals[1] + ' & ' + mask + ')');
|
||||
context.cond.a = test;
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
bne: function(instr, context) {
|
||||
return _conditional(instr, context, 'NE');
|
||||
},
|
||||
beq: function(instr, context) {
|
||||
return _conditional(instr, context, 'EQ');
|
||||
},
|
||||
bgt: function(instr, context) {
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bge: function(instr, context) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
blt: function(instr, context) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
ble: function(instr, context) {
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
bra: function(instr, context) {
|
||||
return Base.nop();
|
||||
},
|
||||
btst: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var and = instr.parsed.opd[0].register == '0x0' ? ' & 1)' : ' & (1 << ' + instr.parsed.opd[0].register + '))';
|
||||
var test = Variable.local('(' + instr.parsed.opd[1].register + and);
|
||||
context.cond.a = test;
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
clr: function(instr, context) {
|
||||
var e = instr.parsed;
|
||||
var dst = _as_pointer(e, 0, false);
|
||||
var op = Base.assign(dst, '0');
|
||||
if (e.opd[0].postadd) {
|
||||
var ptr_reg = e.opd[0].register;
|
||||
return Base.composed([op, Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
},
|
||||
cmp: _compare,
|
||||
cmpi: _compare,
|
||||
eori: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.xor);
|
||||
},
|
||||
jsr: function(instr) {
|
||||
var arg = _is_register(instr.parsed.opd[0].register) ? Variable.functionPointer(instr.parsed.opd[0].register) : instr.parsed.opd[0].register;
|
||||
return Base.call(arg, []);
|
||||
},
|
||||
lea: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var src = instr.string ? Variable.string(instr.string) : _as_pointer(instr.parsed, 0, false, true);
|
||||
var dst = _as_pointer(instr.parsed, 1, false, true);
|
||||
return Base.assign(dst, src);
|
||||
},
|
||||
link: function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var src = e.opd[0].register;
|
||||
var dst = e.opd[1].register;
|
||||
return Base.write_memory(dst == '0x0' ? 'a7' : ('a7 + ' + dst), src, e.bits, false);
|
||||
},
|
||||
lsl: function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var src = _as_pointer(e, 0, false);
|
||||
var dst = _as_pointer(e, 1, false);
|
||||
if (!e.opd[1]) {
|
||||
return Base.shift_left(dst, dst, '1');
|
||||
} else if (e.opd[1].pointer) {
|
||||
var op = Base.shift_left(dst, dst, src);
|
||||
if (e.opd[1].postadd) {
|
||||
var ptr_reg = e.opd[1].register;
|
||||
return Base.composed([op, Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
return Base.shift_left(dst, dst, src);
|
||||
|
||||
},
|
||||
lsr: function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var src = _as_pointer(e, 0, false);
|
||||
var dst = _as_pointer(e, 1, false);
|
||||
if (!e.opd[1]) {
|
||||
return Base.shift_right(dst, dst, '1');
|
||||
} else if (e.opd[1].pointer) {
|
||||
var op = Base.shift_right(dst, dst, src);
|
||||
if (e.opd[1].postadd) {
|
||||
var ptr_reg = e.opd[1].register;
|
||||
return Base.composed([op, Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
}
|
||||
return Base.shift_right(dst, dst, src);
|
||||
},
|
||||
move: function(instr) {
|
||||
instr.setBadJump();
|
||||
var ptr_reg, ops;
|
||||
var e = instr.parsed;
|
||||
var pointer = _as_pointer(e, 0, false, true);
|
||||
var register = _as_pointer(e, 1, false, true);
|
||||
|
||||
if (e.opd[1].postadd) {
|
||||
ptr_reg = e.opd[1].register;
|
||||
ops = [
|
||||
Base.read_memory(pointer, register, e.bits, false),
|
||||
Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))
|
||||
];
|
||||
if (e.opd[0].postadd) {
|
||||
ptr_reg = e.opd[0].register;
|
||||
ops.push(Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8)));
|
||||
}
|
||||
return Base.composed(ops);
|
||||
} else if (e.opd[0].postadd) {
|
||||
ptr_reg = e.opd[0].register;
|
||||
ops = [
|
||||
Base.read_memory(pointer, register, e.bits, false),
|
||||
Base.add(ptr_reg, ptr_reg, '' + (e.bits / 8))
|
||||
];
|
||||
return Base.composed(ops);
|
||||
}
|
||||
return Base.read_memory(pointer, register, e.bits, false);
|
||||
},
|
||||
movea: _move,
|
||||
movem: _move_multiple,
|
||||
moveq: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
return Base.assign(instr.parsed.opd[1].register, instr.parsed.opd[0].register);
|
||||
},
|
||||
or: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.or);
|
||||
},
|
||||
ori: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.or);
|
||||
},
|
||||
pea: function(instr) {
|
||||
instr.setBadJump();
|
||||
var bits = instr.parsed.bits;
|
||||
var pointer = Variable.pointer('a7 -= 4', bits, false);
|
||||
var register = instr.string ? Variable.string(instr.string) : _as_pointer(instr.parsed, 0, false);
|
||||
return Base.write_memory(pointer, register, bits, false);
|
||||
},
|
||||
rts: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.return();
|
||||
},
|
||||
sf: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var dst = _as_pointer(e, 0, false, true);
|
||||
var op = Base.assign(dst, '0');
|
||||
if (e.opd[1].postadd) {
|
||||
return Base.composed([op, Base.add(e.opd[1].register, e.opd[1].register, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
},
|
||||
st: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var dst = _as_pointer(e, 0, false, true);
|
||||
var op = Base.assign(dst, '1');
|
||||
if (e.opd[0].postadd) {
|
||||
return Base.composed([op, Base.add(e.opd[0].register, e.opd[0].register, '' + (e.bits / 8))]);
|
||||
}
|
||||
return op;
|
||||
},
|
||||
sub: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.subtract);
|
||||
},
|
||||
subq: function(instr) {
|
||||
instr.setBadJump();
|
||||
return _common_math(instr.parsed, Base.subtract);
|
||||
},
|
||||
tst: function(instr, context) {
|
||||
instr.setBadJump();
|
||||
context.cond.a = _as_pointer(instr.parsed, 0, false);
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
unlk: function(instr) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
return Base.read_memory(e.opd[1].register || 'a7', e.opd[0].register, e.bits, false);
|
||||
},
|
||||
invalid: function(instr) {
|
||||
instr.setBadJump();
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(asm) {
|
||||
const expr = /([a-zA-Z]+)(\.[bwl])?\s+([\w.:_-]+\([\w. *,[\]]+\)|-?\([\w. *,[\]]+\)\+?|[\w./:_-]+)(?:,\s+([\w.:_-]+\([\w. *,[\]]+\)|-?\([\w. *,[\]]+\)\+?|[\w./:_-]+))?/;
|
||||
var token = asm.match(expr);
|
||||
token.shift();
|
||||
/*
|
||||
* [
|
||||
"move.l obj.__unctrl(d0.l * 4), -(a7)",
|
||||
"move",
|
||||
".l",
|
||||
"obj.__unctrl(d0.l * 4)",
|
||||
"-(a7)"
|
||||
* ]
|
||||
* token[0] = assembly
|
||||
* token[1] = mnem
|
||||
* token[2] = bits
|
||||
* token[3] = opd0
|
||||
* token[4] = opd1
|
||||
*/
|
||||
|
||||
var mnem = token.shift();
|
||||
var bits = token.shift();
|
||||
|
||||
var ret = {
|
||||
mnem: mnem,
|
||||
bits: _to_size(bits ? bits.substring(1) : 'l'),
|
||||
opd: token.filter(function(x) {
|
||||
return x ? true : false;
|
||||
}).map(function(x) {
|
||||
var d = {
|
||||
presub: false,
|
||||
postadd: false,
|
||||
pointer: null,
|
||||
register: null,
|
||||
};
|
||||
|
||||
var isreg = x.indexOf('(') < 0;
|
||||
var ptr = x.replace(/([\w.:_-][\w.:_-]+)\(/, '$1 + ').replace(/(-?\()|\)\+?/g, ' ').replace(/,\sinvalid\.[bwl]/, '');
|
||||
var p = x.replace(/[(),]/g, ' ').replace(/\s+/g, ' ');
|
||||
p = p.replace(/invalid\.[bwl]/, '');
|
||||
p = p.trim().split(' ');
|
||||
if (p[0] == '-') {
|
||||
d.presub = true;
|
||||
p.shift();
|
||||
} else if (p[p.length - 1] == '+') {
|
||||
d.postadd = true;
|
||||
p.pop();
|
||||
}
|
||||
if (isreg) {
|
||||
d.register = p.pop();
|
||||
} else if (ptr.indexOf('-') > 0 || ptr.indexOf('+') > 0 || ptr.indexOf('*') > 0) {
|
||||
d.pointer = ptr.replace(/(\d)\.[bwl]/, '$1').replace(/,/g, ' +').trim();
|
||||
d.register = ptr.match(/[ad]\d+/)[0];
|
||||
} else {
|
||||
d.pointer = p.join(' + ');
|
||||
d.register = p[0];
|
||||
}
|
||||
return d;
|
||||
}),
|
||||
};
|
||||
return ret;
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: '?',
|
||||
b: '?'
|
||||
},
|
||||
leave: false,
|
||||
vars: []
|
||||
};
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
});
|
||||
-1878
File diff suppressed because it is too large.
Load diff
@@ -1,601 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
|
||||
var Base = require('libdec/core/base');
|
||||
var Variable = require('libdec/core/variable');
|
||||
var Long = require('libdec/long');
|
||||
|
||||
function load_bits(instr, bits, unsigned) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var arg = e.opd[1].replace(/\)/, '').split('(');
|
||||
if (arg[0].indexOf('-sym.') == 0 && arg[1] == 'gp') {
|
||||
return Base.assign(e.opd[0], arg[0].replace(/^-/, ''));
|
||||
} else if (arg[1] == '0') {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(arg[0], e.opd[0], bits, !unsigned);
|
||||
} else if (arg[0] == '0') {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(arg[1], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
if (!isNaN(arg[0])) {
|
||||
if (arg[0] < 0) {
|
||||
arg[0] = " - 0x" + Math.abs(arg[0]).toString(16);
|
||||
} else {
|
||||
arg[0] = " + 0x" + arg[0].toString(16);
|
||||
}
|
||||
return Base.read_memory(arg[1] + arg[0], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
return Base.read_memory(arg[1], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
|
||||
function store_bits(instr, bits, unsigned) {
|
||||
instr.setBadJump();
|
||||
var e = instr.parsed;
|
||||
var arg = e.opd[1].replace(/\)/, '').split('(');
|
||||
if (arg[1] == '0') {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(arg[0], e.opd[0], bits, !unsigned);
|
||||
} else if (arg[0] == '0') {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(arg[1], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
arg[0] = arg[0].length > 0 ? parseInt(arg[0]) : NaN;
|
||||
if (!isNaN(arg[0])) {
|
||||
if (arg[0] < 0) {
|
||||
arg[0] = " - 0x" + Math.abs(arg[0]).toString(16);
|
||||
} else {
|
||||
arg[0] = " + 0x" + arg[0].toString(16);
|
||||
}
|
||||
return Base.write_memory(arg[1] + arg[0], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
return Base.write_memory(arg[1], e.opd[0], bits, !unsigned);
|
||||
}
|
||||
|
||||
function _csr_assign_3arg(instr) {
|
||||
instr.setBadJump();
|
||||
var dst = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
return Base.assign(dst, src);
|
||||
}
|
||||
|
||||
function _generic_assign(instr) {
|
||||
instr.setBadJump();
|
||||
var dst = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
return Base.assign(dst, src);
|
||||
}
|
||||
|
||||
function _generic_add(instr) {
|
||||
instr.setBadJump();
|
||||
var dst = instr.parsed.opd[0];
|
||||
var src0 = instr.parsed.opd[1];
|
||||
var src1 = instr.parsed.opd[2];
|
||||
return Base.add(dst, src0, src1);
|
||||
}
|
||||
|
||||
function op_bits3(instr, op) {
|
||||
instr.setBadJump();
|
||||
var dst = instr.parsed.opd[0];
|
||||
var src0 = instr.parsed.opd[1];
|
||||
var src1 = instr.parsed.opd[2];
|
||||
return op(dst, src0, src1);
|
||||
}
|
||||
|
||||
function assign_call(instr, name) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var args = instr.parsed.opd.slice(1);
|
||||
return Base.special(dst + ' = ' + name + '( ', args.join(', ') + ')');
|
||||
}
|
||||
|
||||
function _compare(instr, context, instructions, cmp, zero) {
|
||||
instr.conditional(instr.parsed.opd[0], zero ? "0" : instr.parsed.opd[1], cmp);
|
||||
var arg = instr.parsed.opd[instr.parsed.opd.length - 1];
|
||||
if (arg.indexOf('0x') == 0) {
|
||||
instr.jump = Long.fromString(arg);
|
||||
}
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
function _is_last_instruction(instr, instructions) {
|
||||
return instructions.indexOf(instr) == (instructions.length - 1);
|
||||
}
|
||||
|
||||
function _hex(value) {
|
||||
return parseInt(value).toString(16);
|
||||
}
|
||||
|
||||
function lui32(instr, start, instructions, context) {
|
||||
var addr = null;
|
||||
var check = [
|
||||
function(e, r) {
|
||||
return e.mnem == 'lui' && e.opd[0] == r;
|
||||
},
|
||||
function(e, r) {
|
||||
if (e.mnem == 'nop') {
|
||||
return true;
|
||||
}
|
||||
return (e.mnem == 'ori' && (e.opd[0] == r || e.opd[0].indexOf('a') == 0) && e.opd[1] == r) ||
|
||||
(e.mnem == 'addi' && (e.opd[0] == r || e.opd[0].indexOf('a') == 0) && e.opd[1] == r) ||
|
||||
(e.mnem == 'addiu' && (e.opd[0] == r || e.opd[0].indexOf('a') == 0) && e.opd[1] == r);
|
||||
},
|
||||
];
|
||||
var address = [
|
||||
function(e, addr) {
|
||||
var imm32 = parseInt(instr.parsed.opd[1]).toString(16) << 12;
|
||||
return Long.fromNumber(imm32, true);
|
||||
},
|
||||
function(e, addr) {
|
||||
var n = Long.fromString(_hex(e.opd[2]), e.mnem.indexOf('u') > 0, 16);
|
||||
var op = e.mnem.replace(/[iu]/g, '');
|
||||
return addr[op](n);
|
||||
},
|
||||
];
|
||||
var step = 0;
|
||||
var i;
|
||||
for (i = start; i < instructions.length && step < check.length; ++i) {
|
||||
var elem = instructions[i].parsed;
|
||||
if (!check[step](elem, instr.parsed.opd[0])) {
|
||||
break;
|
||||
}
|
||||
addr = address[step](elem, addr);
|
||||
step++;
|
||||
if (instr.parsed.opd[0] == instructions[i].parsed.opd[0]) {
|
||||
instructions[i].valid = false;
|
||||
} else {
|
||||
instr.valid = true;
|
||||
instr = instructions[i];
|
||||
}
|
||||
}
|
||||
--i;
|
||||
if (instr.parsed.opd[0] != 'gp') {
|
||||
instr.string = Global.xrefs.find_string(addr);
|
||||
instr.symbol = Global.xrefs.find_symbol(addr);
|
||||
addr = instr.string ? Variable.string(instr.string) : (instr.symbol || ('0x' + addr.toString(16)).replace(/0x-/, '-0x'));
|
||||
instr.valid = true;
|
||||
} else {
|
||||
addr = ('0x' + addr.toString(16)).replace(/0x-/, '-0x');
|
||||
instr.valid = false;
|
||||
}
|
||||
instr.code = Base.assign(instr.parsed.opd[0], addr);
|
||||
return i;
|
||||
}
|
||||
|
||||
return {
|
||||
instructions: {
|
||||
auipc: function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var imm32 = parseInt(instr.parsed.opd[1]);
|
||||
var src = instr.location.add(imm32);
|
||||
return Base.assign(dst, '0x' + src.toString(16)) ;
|
||||
},
|
||||
lui: function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var imm20 = parseInt(instr.parsed.opd[1]).toString(16) << 12;
|
||||
return Base.assign(dst, '0x' + imm20.toString(16)) ;
|
||||
},
|
||||
lb: function(instr) {
|
||||
return load_bits(instr, 8, false);
|
||||
},
|
||||
lh: function(instr) {
|
||||
return load_bits(instr, 16, false);
|
||||
},
|
||||
lw: function(instr) {
|
||||
return load_bits(instr, 32, false);
|
||||
},
|
||||
lwu: function(instr) {
|
||||
return load_bits(instr, 32, true);
|
||||
},
|
||||
ld: function(instr) {
|
||||
return load_bits(instr, 64, false);
|
||||
},
|
||||
lbu: function(instr) {
|
||||
return load_bits(instr, 8, true);
|
||||
},
|
||||
lhu: function(instr) {
|
||||
return load_bits(instr, 16, true);
|
||||
},
|
||||
'lr.w': function(instr) {
|
||||
return load_bits(instr, 32, false);
|
||||
},
|
||||
'lr.d': function(instr) {
|
||||
return load_bits(instr, 64, false);
|
||||
},
|
||||
sb: function(instr) {
|
||||
return store_bits(instr, 8, false);
|
||||
},
|
||||
sh: function(instr) {
|
||||
return store_bits(instr, 16, false);
|
||||
},
|
||||
sw: function(instr) {
|
||||
return store_bits(instr, 32, false);
|
||||
},
|
||||
sd: function(instr) {
|
||||
return store_bits(instr, 64, false);
|
||||
},
|
||||
'sc.w': function(instr) {
|
||||
return store_bits(instr, 32, false);
|
||||
},
|
||||
'sc.d': function(instr) {
|
||||
return store_bits(instr, 64, false);
|
||||
},
|
||||
'sext.w': function(instr) {
|
||||
return Base.extend(instr.parsed.opd[0], instr.parsed.opd[1], 32);
|
||||
},
|
||||
add: _generic_add,
|
||||
addi: _generic_add,
|
||||
addw: _generic_add,
|
||||
addiw: _generic_add,
|
||||
and: function(instr) {
|
||||
return op_bits3(instr, Base.and);
|
||||
},
|
||||
andi: function(instr) {
|
||||
return op_bits3(instr, Base.and);
|
||||
},
|
||||
sub: function(instr) {
|
||||
return op_bits3(instr, Base.subtract);
|
||||
},
|
||||
subw: function(instr) {
|
||||
return op_bits3(instr, Base.subtract);
|
||||
},
|
||||
mul: function(instr) {
|
||||
return op_bits3(instr, Base.multiply);
|
||||
},
|
||||
mulh: function(instr) {
|
||||
return op_bits3(instr, Base.multiply);
|
||||
},
|
||||
mulw: function(instr) {
|
||||
return op_bits3(instr, Base.multiply);
|
||||
},
|
||||
srli: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
srai: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
sll: function(instr) {
|
||||
return op_bits3(instr, Base.shift_left);
|
||||
},
|
||||
slli: function(instr) {
|
||||
return op_bits3(instr, Base.shift_left);
|
||||
},
|
||||
srlw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
srliw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
sraw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
sraiw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_right);
|
||||
},
|
||||
sllw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_left);
|
||||
},
|
||||
slliw: function(instr) {
|
||||
return op_bits3(instr, Base.shift_left);
|
||||
},
|
||||
div: function(instr) {
|
||||
return op_bits3(instr, Base.divide);
|
||||
},
|
||||
divu: function(instr) {
|
||||
return op_bits3(instr, Base.divide);
|
||||
},
|
||||
rem: function(instr) {
|
||||
return op_bits3(instr, Base.module);
|
||||
},
|
||||
remu: function(instr) {
|
||||
return op_bits3(instr, Base.module);
|
||||
},
|
||||
divw: function(instr) {
|
||||
return op_bits3(instr, Base.divide);
|
||||
},
|
||||
divuw: function(instr) {
|
||||
return op_bits3(instr, Base.divide);
|
||||
},
|
||||
remw: function(instr) {
|
||||
return op_bits3(instr, Base.module);
|
||||
},
|
||||
remuw: function(instr) {
|
||||
return op_bits3(instr, Base.module);
|
||||
},
|
||||
or: function(instr) {
|
||||
return op_bits3(instr, Base.or);
|
||||
},
|
||||
ori: function(instr) {
|
||||
return op_bits3(instr, Base.or);
|
||||
},
|
||||
xor: function(instr) {
|
||||
return op_bits3(instr, Base.xor);
|
||||
},
|
||||
xori: function(instr) {
|
||||
return op_bits3(instr, Base.xor);
|
||||
},
|
||||
'slt': function(instr) {
|
||||
var e = instr.parsed;
|
||||
return Base.conditional_assign(e.opd[0], e.opd[1], e.opd[2], 'LT', '1', '0');
|
||||
},
|
||||
'slti': function(instr) {
|
||||
var e = instr.parsed;
|
||||
return Base.conditional_assign(e.opd[0], e.opd[1], e.opd[2], 'LT', '1', '0');
|
||||
},
|
||||
'sltiu': function(instr) {
|
||||
var e = instr.parsed;
|
||||
var arg0 = Variable.local(e.opd[1], 32);
|
||||
var arg1 = Variable.local(e.opd[2], 32);
|
||||
return Base.conditional_assign(e.opd[0], arg0, arg1, 'LT', '1', '0');
|
||||
},
|
||||
'sltu': function(instr) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[2] == 'zero') {
|
||||
e.opd[2] = '0';
|
||||
}
|
||||
var arg0 = Variable.local(e.opd[1], 32);
|
||||
var arg1 = Variable.local(e.opd[2], 32);
|
||||
return Base.conditional_assign(e.opd[0], arg0, arg1, 'LT', '1', '0');
|
||||
},
|
||||
'amoadd.w': _generic_add,
|
||||
'amoor.w': function(instr) {
|
||||
return op_bits3(instr, Base.or);
|
||||
},
|
||||
'amoxor.w': function(instr) {
|
||||
return op_bits3(instr, Base.xor);
|
||||
},
|
||||
'amoand.w': function(instr) {
|
||||
return op_bits3(instr, Base.and);
|
||||
},
|
||||
'amomin.w': function(instr) {
|
||||
return assign_call(instr, 'min');
|
||||
},
|
||||
'amominu.w': function(instr) {
|
||||
return assign_call(instr, 'min');
|
||||
},
|
||||
'amomax.w': function(instr) {
|
||||
return assign_call(instr, 'max');
|
||||
},
|
||||
'amomaxu.w': function(instr) {
|
||||
return assign_call(instr, 'max');
|
||||
},
|
||||
'amoadd.d': _generic_add,
|
||||
'amoor.d': function(instr) {
|
||||
return op_bits3(instr, Base.or);
|
||||
},
|
||||
'amoxor.d': function(instr) {
|
||||
return op_bits3(instr, Base.xor);
|
||||
},
|
||||
'amoand.d': function(instr) {
|
||||
return op_bits3(instr, Base.and);
|
||||
},
|
||||
'amomin.d': function(instr) {
|
||||
return assign_call(instr, 'min');
|
||||
},
|
||||
'amominu.d': function(instr) {
|
||||
return assign_call(instr, 'min');
|
||||
},
|
||||
'amomax.d': function(instr) {
|
||||
return assign_call(instr, 'max');
|
||||
},
|
||||
'amomaxu.d': function(instr) {
|
||||
return assign_call(instr, 'max');
|
||||
},
|
||||
mv: _generic_assign,
|
||||
li: _generic_assign,
|
||||
csrr: _generic_assign,
|
||||
csrw: _generic_assign,
|
||||
csrc: _generic_assign,
|
||||
csrs: _generic_assign,
|
||||
csrri: _generic_assign,
|
||||
csrwi: _generic_assign,
|
||||
csrci: _generic_assign,
|
||||
csrsi: _generic_assign,
|
||||
csrrwi: _csr_assign_3arg,
|
||||
csrrci: _csr_assign_3arg,
|
||||
csrrsi: _csr_assign_3arg,
|
||||
beqz: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'EQ', true);
|
||||
},
|
||||
bnez: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'NE', true);
|
||||
},
|
||||
bltz: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LT', true);
|
||||
},
|
||||
blez: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LE', true);
|
||||
},
|
||||
bgtz: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GT', true);
|
||||
},
|
||||
bgez: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GE', true);
|
||||
},
|
||||
beq: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'EQ', false);
|
||||
},
|
||||
bne: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'NE', false);
|
||||
},
|
||||
blt: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LT', false);
|
||||
},
|
||||
ble: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LE', false);
|
||||
},
|
||||
bgt: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GT', false);
|
||||
},
|
||||
bge: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GE', false);
|
||||
},
|
||||
bltu: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LT', false);
|
||||
},
|
||||
bleu: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'LE', false);
|
||||
},
|
||||
bgtu: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GT', false);
|
||||
},
|
||||
bgeu: function(instr, context, instructions) {
|
||||
return _compare(instr, context, instructions, 'GE', false);
|
||||
},
|
||||
jal: function(instr, context, instructions) {
|
||||
var fcn_name = instr.parsed.opd[1].replace(/\./g, '_');
|
||||
if (fcn_name.indexOf('0x') == 0) {
|
||||
fcn_name = fcn_name.replace(/0x/, 'fcn_');
|
||||
}
|
||||
var args = [];
|
||||
var regs = ['a7', 'a6', 'a5', 'a4', 'a3', 'a2', 'a1', 'a0'];
|
||||
var found = 0, i;
|
||||
for (i = instructions.indexOf(instr) - 1; i >= 0; i--) {
|
||||
if (instructions[i].parsed.mnem.startsWith('b') ||
|
||||
instructions[i].parsed.mnem.startsWith('j')) {
|
||||
break;
|
||||
}
|
||||
var reg = instructions[i].parsed.opd[0];
|
||||
if (regs.indexOf(reg) >= 0) {
|
||||
found = parseInt(reg.substr(1, 3)) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < found; i++) {
|
||||
args.push('a' + i);
|
||||
}
|
||||
return Base.call(fcn_name, args);
|
||||
},
|
||||
jalr: function(instr) {
|
||||
var is_return = false;
|
||||
var arg = instr.parsed.opd[0];
|
||||
if (arg.indexOf('(') > 0) {
|
||||
arg = arg.replace(/\(|\)/g, ' ').trim().split(' ');
|
||||
if (arg[1] == 'ra') {
|
||||
is_return = true;
|
||||
}
|
||||
arg = arg[1] + ' + ' + arg[0];
|
||||
arg = arg.replace(/\+ -/, '-');
|
||||
}
|
||||
if (is_return || arg == 'ra') {
|
||||
if (is_return) {
|
||||
instr.comments.push('return address: ' + arg);
|
||||
}
|
||||
return Base.return(instr.parsed.opd[1]);
|
||||
}
|
||||
return Base.call(arg, [], true);
|
||||
},
|
||||
j: function(instr, context, instructions) {
|
||||
var arg = instr.parsed.opd[0];
|
||||
if (arg.indexOf('0x') == 0) {
|
||||
instr.jump = Long.fromString(arg);
|
||||
} else if (_is_last_instruction(instr, instructions)) {
|
||||
return Base.call(arg, []);
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
ret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
uret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
sret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
mret: function(instr) {
|
||||
return Base.return();
|
||||
},
|
||||
fence: function() {
|
||||
return Base.macro('memory_fence()', '#define memory_fence() __asm(fence)');
|
||||
},
|
||||
'fence.i': function() {
|
||||
return Base.macro('memory_fence_i()', '#define memory_fence_i() __asm(fence.i)');
|
||||
},
|
||||
'sfence.vma': function() {
|
||||
return Base.macro('memory_sfence_vma()', '#define memory_sfence_vma() __asm(sfence.vma)');
|
||||
},
|
||||
ebreak: function() {
|
||||
return Base.macro('environment_break()', '#define environment_break() __asm(ebreak)');
|
||||
},
|
||||
ecall: function() {
|
||||
return Base.macro('environment_call()', '#define environment_call() __asm(ecall)');
|
||||
},
|
||||
wfi: function() {
|
||||
return Base.macro('wait_for_interrupt()', '#define wait_for_interrupt() __asm(wfi)');
|
||||
},
|
||||
nop: function(instr) {
|
||||
return Base.nop();
|
||||
},
|
||||
invalid: function() {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(asm) {
|
||||
asm = asm.replace(/,/g, ' ').replace(/\s+/g, ' ').trim().split(' ').map(function(x) {
|
||||
if (x == 'zero') {
|
||||
return '0';
|
||||
}
|
||||
return x;
|
||||
});
|
||||
|
||||
var mnem = asm.shift();
|
||||
if (mnem.indexOf('amo') == 0) {
|
||||
// atomic ops.
|
||||
asm = asm.map(function(x) {
|
||||
return x.replace(/\(|\)/g, '');
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
mnem: mnem,
|
||||
opd: asm
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: null,
|
||||
b: null
|
||||
}
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions) {
|
||||
},
|
||||
postanalisys: function(instructions, context) {
|
||||
/* simplifies any load address 32/64 bit */
|
||||
for (var i = 0; i < instructions.length; i++) {
|
||||
if (instructions[i].parsed.mnem == 'lui') {
|
||||
i = lui32(instructions[i], i, instructions);
|
||||
}
|
||||
}
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,561 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019-2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
// https://www.rockbox.org/wiki/pub/Main/DataSheets/sh1_2p.pdf
|
||||
// http://www.shared-ptr.com/sh_insns.html
|
||||
|
||||
|
||||
const Instruction = require('libdec/core/instruction');
|
||||
const Base = require('libdec/core/base');
|
||||
const Variable = require('libdec/core/variable');
|
||||
//const Extra = require('libdec/core/extra');
|
||||
|
||||
const _s = 's';
|
||||
const _carry = 'c';
|
||||
const _mac = 'mac';
|
||||
|
||||
const _size_map = {
|
||||
"b": 8,
|
||||
"w": 16,
|
||||
"l": 32
|
||||
};
|
||||
|
||||
const _compare_map = {
|
||||
'eq': 'EQ',
|
||||
'hi': 'GT',
|
||||
'hs': 'GE',
|
||||
'ge': 'GE',
|
||||
'gt': 'GT',
|
||||
'pl': 'GT',
|
||||
'pz': 'GE',
|
||||
'str': 'str',
|
||||
};
|
||||
|
||||
const _reverse_map = {
|
||||
'EQ': 'NE',
|
||||
'GT': 'LE',
|
||||
'GE': 'LT',
|
||||
};
|
||||
|
||||
const _branch_list = [
|
||||
'bra',
|
||||
'braf',
|
||||
'bsr',
|
||||
'bsrf',
|
||||
'jmp',
|
||||
'jsr',
|
||||
'rte',
|
||||
'rts'
|
||||
];
|
||||
|
||||
function _set_conditional(instr, context, cmp, invert) {
|
||||
if (invert) {
|
||||
cmp = _reverse_map[cmp];
|
||||
}
|
||||
instr.conditional(context.cond.a, context.cond.b, cmp);
|
||||
return Base.nop();
|
||||
}
|
||||
|
||||
function _require_composed(instr, idx) {
|
||||
var e = instr.parsed;
|
||||
return e.opd[idx].increment || e.opd[idx].decrement;
|
||||
}
|
||||
|
||||
function _handle_pointer(instr, idx, ops) {
|
||||
var e = instr.parsed;
|
||||
var size = e.size || 32;
|
||||
if (e.opd[idx].increment) {
|
||||
ops.push(Base.increase(e.opd[idx].token, size / 8));
|
||||
} else if (e.opd[idx].decrement) {
|
||||
ops.unshift(Base.decrease(e.opd[idx].token, size / 8));
|
||||
}
|
||||
}
|
||||
|
||||
function _arg(instr, idx) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[idx].pointer) {
|
||||
// a great way to fix PC + N
|
||||
if (e.opd[idx].token.startsWith('pc +')) {
|
||||
var n = parseInt(e.opd[idx].token.substr('pc +'.length));
|
||||
var p = instr.location.add(n);
|
||||
e.opd[idx].token = '0x' + p.toString(16);
|
||||
}
|
||||
return Variable.pointer(e.opd[idx].toString(), e.size || 32, true);
|
||||
}
|
||||
return e.opd[idx].toString();
|
||||
}
|
||||
|
||||
function _op_move(instr) {
|
||||
var ops = [];
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
if (instr.parsed.size) {
|
||||
ops.push(Base.cast(dst, src, 'int' + instr.parsed.size + '_t'));
|
||||
} else {
|
||||
ops.push(Base.assign(dst, src));
|
||||
}
|
||||
if (_require_composed(instr, 0) || _require_composed(instr, 1)) {
|
||||
if (_require_composed(instr, 0)) {
|
||||
_handle_pointer(instr, 0, ops);
|
||||
} else {
|
||||
_handle_pointer(instr, 1, ops);
|
||||
}
|
||||
return Base.composed(ops);
|
||||
}
|
||||
return ops[0];
|
||||
}
|
||||
|
||||
return {
|
||||
instructions: {
|
||||
mov: _op_move,
|
||||
mova: _op_move,
|
||||
movt: function(instr, context, instructions) {
|
||||
instr.parsed.opd[0].token = _carry;
|
||||
return _op_move(instr);
|
||||
},
|
||||
swap: function(instr, context, instructions) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.swap(dst, src, 32);
|
||||
},
|
||||
/*
|
||||
xtrct: function(instr, context, instructions) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.?????(dst, src);
|
||||
},
|
||||
*/
|
||||
add: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.add(dst, dst, src);
|
||||
},
|
||||
addc: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.add(dst, dst, src + ' + ' + _carry);
|
||||
},
|
||||
addv: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
instr.comments.push('if overflows, then ' + _carry + ' = 1.');
|
||||
return Base.add(dst, dst, src + ' + ' + _carry);
|
||||
},
|
||||
cmp: function(instr, context) {
|
||||
var a = _arg(instr, 0);
|
||||
var b = _arg(instr, 1);
|
||||
if (instr.parsed.cmp != 'str') {
|
||||
context.cond.a = a;
|
||||
context.cond.b = b;
|
||||
context.cond.cmp = instr.parsed.cmp;
|
||||
} else {
|
||||
context.cond.a = (a === b) ? a : '(' + a + ' & ' + b + ')';
|
||||
context.cond.b = '0';
|
||||
context.cond.cmp = 'EQ';
|
||||
}
|
||||
},
|
||||
tst: function(instr, context) {
|
||||
var a = _arg(instr, 0);
|
||||
var b = _arg(instr, 1);
|
||||
context.cond.a = (a === b) ? a : '(' + a + ' & ' + b + ')';
|
||||
context.cond.b = '0';
|
||||
context.cond.cmp = 'EQ';
|
||||
},
|
||||
div1: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.divide(dst, dst, src);
|
||||
},
|
||||
dmuls: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(dst, dst, src);
|
||||
},
|
||||
dt: function(instr) {
|
||||
var dst = _arg(instr, 1);
|
||||
return Base.decrease(dst, dst, 1);
|
||||
},
|
||||
exts: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.cast(dst, src, 'int' + instr.parsed.size + '_t');
|
||||
},
|
||||
extu: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.cast(dst, src, 'uint' + instr.parsed.size + '_t');
|
||||
},
|
||||
mul: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(_mac, dst, src);
|
||||
},
|
||||
muls: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(_mac, dst, src);
|
||||
},
|
||||
mulu: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(_mac, dst, src);
|
||||
},
|
||||
neg: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.negate(dst, src);
|
||||
},
|
||||
negc: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.composed([
|
||||
Base.negate(dst, src),
|
||||
Base.subtract(dst, dst, _carry),
|
||||
]);
|
||||
},
|
||||
sub: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.subtract(dst, dst, src);
|
||||
},
|
||||
subc: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.composed([
|
||||
Base.subtract(dst, dst, src),
|
||||
Base.subtract(dst, dst, _carry),
|
||||
]);
|
||||
},
|
||||
subv: function(instr) {
|
||||
instr.comments.push('if underflows, then ' + _carry + ' = 1.');
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.subtract(dst, dst, src);
|
||||
},
|
||||
and: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.and(dst, dst, src);
|
||||
},
|
||||
not: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.not(dst, src);
|
||||
},
|
||||
or: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.or(dst, dst, src);
|
||||
},
|
||||
xor: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.xor(dst, dst, src);
|
||||
},
|
||||
rotl: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.rotate_left(src, src, 1, 32);
|
||||
},
|
||||
rotr: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.rotate_right(src, src, 1, 32);
|
||||
},
|
||||
rotcl: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.rotate_left(src, src, 1, 32);
|
||||
},
|
||||
rotcr: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.rotate_right(src, src, 1, 32);
|
||||
},
|
||||
shal: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 1);
|
||||
},
|
||||
shar: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 1);
|
||||
},
|
||||
shll: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 1);
|
||||
},
|
||||
shlr: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 1);
|
||||
},
|
||||
shll2: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 2);
|
||||
},
|
||||
shlr2: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 2);
|
||||
},
|
||||
shll8: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 8);
|
||||
},
|
||||
shlr8: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 8);
|
||||
},
|
||||
shll16: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_left(src, src, 16);
|
||||
},
|
||||
shlr16: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.shift_right(src, src, 16);
|
||||
},
|
||||
ldc: _op_move,
|
||||
lds: _op_move,
|
||||
stc: _op_move,
|
||||
sts: _op_move,
|
||||
clrt: function(instr) {
|
||||
return Base.assign(_carry, '0');
|
||||
},
|
||||
clrs: function(instr) {
|
||||
return Base.assign(_s, '0');
|
||||
},
|
||||
sett: function(instr) {
|
||||
return Base.assign(_carry, '1');
|
||||
},
|
||||
sets: function(instr) {
|
||||
return Base.assign(_s, '1');
|
||||
},
|
||||
fmov: _op_move,
|
||||
flds: _op_move,
|
||||
fsts: _op_move,
|
||||
fldi0: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.assign(src, '0.0');
|
||||
},
|
||||
fldi1: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.assign(src, '1.0');
|
||||
},
|
||||
fabs: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.and(src, src, '0x7FFFFFFF');
|
||||
},
|
||||
fneg: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.negate(dst, src);
|
||||
},
|
||||
fadd: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.add(dst, dst, src);
|
||||
},
|
||||
fsub: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.subtract(dst, dst, src);
|
||||
},
|
||||
fmul: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.multiply(dst, dst, src);
|
||||
},
|
||||
fdiv: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.divide(dst, dst, src);
|
||||
},
|
||||
fsqrt: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.assign(dst, Base.call('sqrt', [src]));
|
||||
},
|
||||
fcmp: function(instr, context) {
|
||||
var a = _arg(instr, 0);
|
||||
var b = _arg(instr, 1);
|
||||
context.cond.a = a;
|
||||
context.cond.b = b;
|
||||
context.cond.cmp = instr.parsed.cmp;
|
||||
},
|
||||
float: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.cast(dst, src, 'float');
|
||||
},
|
||||
ftrc: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.cast(dst, src, 'int32_t');
|
||||
},
|
||||
fsrra: function(instr) {
|
||||
var dst = _arg(instr, 0);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.special(dst + ' = 1.0 / sqrt (' + src + ')');
|
||||
},
|
||||
fsca: function(instr) {
|
||||
var dst1 = _arg(instr, 0);
|
||||
var dst2 = 'fr' + (parseInt(dst1.substr(2)) + 1).toString(16);
|
||||
var src = _arg(instr, 1);
|
||||
return Base.composed([
|
||||
Base.assign(dst1, Base.call('sin', [src])),
|
||||
Base.assign(dst2, Base.call('cos', [src]))
|
||||
]);
|
||||
},
|
||||
bt: function(instr, context, instructions) {
|
||||
return _set_conditional(instr, context, 'EQ');
|
||||
},
|
||||
bf: function(instr, context, instructions) {
|
||||
return _set_conditional(instr, context, 'NE');
|
||||
},
|
||||
jsr: function(instr, context, instructions) {
|
||||
var e = instr.parsed.opd;
|
||||
return Base.call(Variable.functionPointer(e[1].token), []);
|
||||
},
|
||||
bra: function(instr) {
|
||||
var callname = _arg(instr, 1);
|
||||
if (callname.startsWith('0x')) {
|
||||
callname = Variable.functionPointer(callname);
|
||||
}
|
||||
return Base.call(callname, []);
|
||||
},
|
||||
braf: function(instr) {
|
||||
var src = _arg(instr, 1);
|
||||
return Base.call(Variable.functionPointer(src), []);
|
||||
},
|
||||
rte: function() {
|
||||
return Base.return();
|
||||
},
|
||||
rts: function() {
|
||||
return Base.return();
|
||||
},
|
||||
nop: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
invalid: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(assembly) {
|
||||
assembly = assembly.trim().toLowerCase();
|
||||
assembly = assembly.replace(/,/g, ' ');
|
||||
assembly = assembly.replace(/\s+/g, ' ');
|
||||
|
||||
/*
|
||||
Regex explaination:
|
||||
^
|
||||
(\w+)
|
||||
\.?
|
||||
([wlb])? // word/long/byte
|
||||
([sn])? // delayed branch
|
||||
\/?
|
||||
(hi|eq|hs|pl|pz|str)? // compare flags
|
||||
? // space might not exists (aka no 1st and 2nd param)
|
||||
(
|
||||
(
|
||||
(@)? // Indirect or Direct access
|
||||
(
|
||||
(\(\w+\s\w+\)) A + B value
|
||||
|
|
||||
(-)? Pre-decrement
|
||||
([\w._]+) register
|
||||
(\+)? Post-increment
|
||||
)
|
||||
)
|
||||
)?
|
||||
? // space might not exists (aka no 2nd param)
|
||||
(
|
||||
(
|
||||
(@)? // Indirect or Direct access
|
||||
(
|
||||
(\(\w+\s\w+\)) A + B value
|
||||
|
|
||||
(-)? Pre-decrement
|
||||
([\w._]+) register
|
||||
(\+)? Post-increment
|
||||
)
|
||||
)
|
||||
)?
|
||||
$
|
||||
*/
|
||||
var tokens = assembly.match(/^(\w+)\.?([wlb])?([sn])?\/?(hi|eq|hs|pl|pz|str)? ?(((@)?((\(\w+\s\w+\))|(-)?([\w._]+)(\+)?)))? ?(((@)?((\(\w+\s\w+\))|(-)?([\w._]+)(\+)?)))?$/);
|
||||
var mnem = tokens[1];
|
||||
var size = _size_map[tokens[2]];
|
||||
var delayed = tokens[3] == 's';
|
||||
var cmp = _compare_map[tokens[4]];
|
||||
var opd = [{
|
||||
pointer: tokens[7] == '@',
|
||||
decrement: tokens[10] == '-',
|
||||
increment: tokens[12] == '+',
|
||||
token: tokens[11] || (tokens[9] ? tokens[9].replace(/\((\w+)\s(\w+)\)/g, '$2 + $1') : null),
|
||||
toString: function() {
|
||||
return this.token || '(null)';
|
||||
}
|
||||
}, {
|
||||
pointer: tokens[15] == '@',
|
||||
decrement: tokens[18] == '-',
|
||||
increment: tokens[20] == '+',
|
||||
token: tokens[19] || (tokens[17] ? tokens[17].replace(/\((\w+)\s(\w+)\)/g, '$2 + $1') : null),
|
||||
toString: function() {
|
||||
return this.token || '(null)';
|
||||
}
|
||||
}];
|
||||
|
||||
return {
|
||||
mnem: mnem,
|
||||
delayed: delayed,
|
||||
cmp: cmp,
|
||||
size: size,
|
||||
opd: opd.reverse()
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
cmp: null,
|
||||
a: '?',
|
||||
b: '?'
|
||||
}
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions, context) {
|
||||
/* delayed branch fix */
|
||||
for (var i = 0; i < (instructions.length - 1); i++) {
|
||||
var op = instructions[i].parsed.mnem;
|
||||
if (_branch_list.indexOf(op) >= 0 && instructions[i + 1].parsed.mnem != 'nop') {
|
||||
Instruction.swap(instructions, i, i + 1);
|
||||
++i;
|
||||
} else if (instructions[i].parsed.delayed && instructions[i + 1].parsed.mnem != 'nop') {
|
||||
Instruction.swap(instructions, i, i + 1);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
},
|
||||
postanalisys: function(instructions, context) {},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,412 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
|
||||
var Instruction = require('libdec/core/instruction');
|
||||
var Base = require('libdec/core/base');
|
||||
var Variable = require('libdec/core/variable');
|
||||
var Long = require('libdec/long');
|
||||
|
||||
var _call_fix_name = function(name) {
|
||||
if (typeof name != 'string') {
|
||||
return name;
|
||||
}
|
||||
if (name.indexOf('fcn.') == 0 || name.indexOf('func.') == 0) {
|
||||
return name.replace(/[.:]/g, '_').replace(/__+/g, '_');
|
||||
}
|
||||
return name.replace(/\[reloc\.|\]/g, '').replace(/[.:]/g, '_').replace(/__+/g, '_').replace(/_[0-9a-f]+$/, '').replace(/^_+/, '');
|
||||
};
|
||||
|
||||
var _load = function(instr, context, instruction, signed, bits) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[0].indexOf('+') > 0) {
|
||||
var arg = e.opd[0].split('+');
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
if (arg[1].indexOf('-') < 0) {
|
||||
ops.push(Base.add(value, arg[0], arg[1]));
|
||||
} else {
|
||||
ops.push(Base.subtract(value, arg[0], arg[1].replace(/-/, '')));
|
||||
}
|
||||
ops.push(Base.read_memory(value, e.opd[1], bits, signed));
|
||||
return Base.composed(ops);
|
||||
}
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(e.opd[0], e.opd[1], bits, signed);
|
||||
};
|
||||
|
||||
var _store = function(instr, context, instruction, signed, bits) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[1].indexOf('+') > 0) {
|
||||
var arg = e.opd[1].split('+');
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
if (arg[1].indexOf('-') < 0) {
|
||||
ops.push(Base.add(value, arg[0], arg[1]));
|
||||
} else {
|
||||
ops.push(Base.subtract(value, arg[0], arg[1].replace(/-/, '')));
|
||||
}
|
||||
ops.push(Base.write_memory(value, e.opd[0], bits, signed));
|
||||
return Base.composed(ops);
|
||||
}
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(e.opd[1], e.opd[0], bits, signed);
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, type) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _compare = function(instr, context, instructions) {
|
||||
context.cond.a = instr.parsed.opd[0];
|
||||
context.cond.b = instr.parsed.opd[1];
|
||||
};
|
||||
|
||||
var sethi32 = function(instr, start, instructions) {
|
||||
var addr = null;
|
||||
var check = [
|
||||
function(e, r) {
|
||||
return e.mnem == 'sethi' && e.opd[1] == r;
|
||||
},
|
||||
function(e, r) {
|
||||
if (e.mnem == 'nop') {
|
||||
return true;
|
||||
}
|
||||
return (e.mnem == 'or' && e.opd[0] == r && e.opd[2] == r) || (e.mnem == 'add' && e.opd[0] == r && e.opd[2] == r);
|
||||
}
|
||||
];
|
||||
var address = [
|
||||
function(e, addr) {
|
||||
var v = Long.fromString(parseInt(e.opd[0]).toString(16), false, 16);
|
||||
return v.shl(10);
|
||||
},
|
||||
function(e, addr) {
|
||||
var n = Long.fromString(parseInt(e.opd[1]).toString(16), false, 16);
|
||||
return addr[e.mnem](n);
|
||||
},
|
||||
];
|
||||
var step = 0;
|
||||
var i;
|
||||
for (i = start; i < instructions.length && step < check.length; ++i) {
|
||||
var elem = instructions[i].parsed;
|
||||
if (!check[step](elem, instr.parsed.opd[1])) {
|
||||
break;
|
||||
}
|
||||
addr = address[step](elem, addr);
|
||||
step++;
|
||||
instructions[i].code = Base.nop();
|
||||
}
|
||||
if (addr) {
|
||||
--i;
|
||||
addr = '0x' + addr.toString(16);
|
||||
instr.code = Base.assign(instr.parsed.opd[1], addr.replace(/0x-/, '-0x'));
|
||||
}
|
||||
return i;
|
||||
};
|
||||
|
||||
var _branch_list = [
|
||||
'ba', 'bn', 'be', 'bne', 'bl', 'ble', 'bge', 'bg',
|
||||
'blu', 'bleu', 'bgeu', 'bgu', 'bpos', 'bneg',
|
||||
'bcs', 'bcc', 'bvs', 'bvc', 'call', 'jmp', 'jmpl',
|
||||
'ret', 'retl', 'rett'
|
||||
];
|
||||
|
||||
return {
|
||||
instructions: {
|
||||
add: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
addcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
addx: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
addxcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
and: function(instr, context, instructions) {
|
||||
return Base.and(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
andcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
return Base.and(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
andn: function(instr, context, instructions) {
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
ops.push(Base.not(value, instr.parsed.opd[1]));
|
||||
ops.push(Base.and(instr.parsed.opd[2], instr.parsed.opd[0], value));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
andncc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
ops.push(Base.not(value, instr.parsed.opd[1]));
|
||||
ops.push(Base.and(instr.parsed.opd[2], instr.parsed.opd[0], value));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
ba: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
bn: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
be: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'EQ');
|
||||
},
|
||||
bne: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'NE');
|
||||
},
|
||||
bg: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bge: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
bl: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
ble: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
blu: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
bleu: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
bgeu: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
bgu: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bpos: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
bneg: function(instr, context, instructions) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
call: function(instr, context, instructions) {
|
||||
return Base.call(_call_fix_name(instr.parsed.opd[0]), []);
|
||||
},
|
||||
cmp: _compare,
|
||||
ldub: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, false, 8);
|
||||
},
|
||||
ldsb: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, true, 8);
|
||||
},
|
||||
lduh: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, false, 16);
|
||||
},
|
||||
ldsh: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, true, 16);
|
||||
},
|
||||
ld: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, false, 32);
|
||||
},
|
||||
ldd: function(instr, context, instructions) {
|
||||
return _load(instr, context, instructions, false, 64);
|
||||
},
|
||||
mov: function(instr, context, instructions) {
|
||||
return Base.assign(instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
nop: function(instr) {
|
||||
return Base.nop();
|
||||
},
|
||||
or: function(instr, context, instructions) {
|
||||
return Base.or(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
orcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
return Base.or(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
orn: function(instr, context, instructions) {
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
ops.push(Base.not(value, instr.parsed.opd[1]));
|
||||
ops.push(Base.or(instr.parsed.opd[2], instr.parsed.opd[0], value));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
orncc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
var ops = [];
|
||||
var value = Variable.uniqueName('local');
|
||||
ops.push(Base.not(value, instr.parsed.opd[1]));
|
||||
ops.push(Base.or(instr.parsed.opd[2], instr.parsed.opd[0], value));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
ret: function(instr, context, instructions) {
|
||||
return Base.return();
|
||||
},
|
||||
retl: function(instr, context, instructions) {
|
||||
return Base.return();
|
||||
},
|
||||
rett: function(instr, context, instructions) {
|
||||
return Base.return();
|
||||
},
|
||||
restore: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
save: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
sethi: function(instr, context, instructions) {
|
||||
var v = Long.fromString(parseInt(instr.parsed.opd[0]).toString(16), true, 16);
|
||||
return Base.assign(instr.parsed.opd[1], '0x' + v.shl(10).toString(16));
|
||||
},
|
||||
sll: function(instr, context, instructions) {
|
||||
return Base.shift_left(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
srl: function(instr, context, instructions) {
|
||||
return Base.shift_right(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
sra: function(instr, context, instructions) {
|
||||
return Base.shift_right(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
stb: function(instr, context, instructions) {
|
||||
return _store(instr, context, instructions, false, 8);
|
||||
},
|
||||
sth: function(instr, context, instructions) {
|
||||
return _store(instr, context, instructions, false, 16);
|
||||
},
|
||||
st: function(instr, context, instructions) {
|
||||
return _store(instr, context, instructions, false, 32);
|
||||
},
|
||||
std: function(instr, context, instructions) {
|
||||
return _store(instr, context, instructions, false, 64);
|
||||
},
|
||||
sub: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
subcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
subx: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
subxcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
if (instr.parsed.opd[1].indexOf('-') >= 0) {
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1].replace(/-/, ''));
|
||||
}
|
||||
return Base.subtract(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
xor: function(instr, context, instructions) {
|
||||
return Base.xor(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
xorcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
return Base.xor(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]);
|
||||
},
|
||||
xnor: function(instr, context, instructions) {
|
||||
var ops = [];
|
||||
ops.push(Base.xor(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.not(instr.parsed.opd[2], instr.parsed.opd[2]));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
xnorcc: function(instr, context, instructions) {
|
||||
_compare(instr, context);
|
||||
var ops = [];
|
||||
ops.push(Base.xor(instr.parsed.opd[2], instr.parsed.opd[0], instr.parsed.opd[1]));
|
||||
ops.push(Base.not(instr.parsed.opd[2], instr.parsed.opd[2]));
|
||||
return Base.composed(ops);
|
||||
},
|
||||
unimp: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
invalid: function() {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(assembly) {
|
||||
var tokens = assembly.replace(/,/g, ' ').replace(/\[|\]/g, '').replace(/\s+/g, ' ').trim().split(' ');
|
||||
return {
|
||||
mnem: tokens.shift(),
|
||||
opd: tokens
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: null,
|
||||
b: null
|
||||
}
|
||||
};
|
||||
},
|
||||
preanalisys: function(instructions) {
|
||||
/* delayed branch fix */
|
||||
for (var i = 0; i < (instructions.length - 1); i++) {
|
||||
var op = instructions[i].parsed.mnem;
|
||||
var n = instructions[i + 1].parsed.mnem;
|
||||
if (_branch_list.indexOf(op) >= 0 && n != 'nop' && _branch_list.indexOf(n) < 0) {
|
||||
Instruction.swap(instructions, i, i + 1);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
},
|
||||
postanalisys: function(instructions, context) {
|
||||
/* simplifies any load address 32/64 bit */
|
||||
for (var i = 0; i < instructions.length; i++) {
|
||||
if (instructions[i].parsed.mnem == 'sethi') {
|
||||
i = sethi32(instructions[i], i, instructions);
|
||||
}
|
||||
}
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,533 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
|
||||
var Base = require('libdec/core/base');
|
||||
var Variable = require('libdec/core/variable');
|
||||
|
||||
var _common_math = function(e, op, reversed) {
|
||||
if (e.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
if (reversed) {
|
||||
return op(e.opd[1], e.opd[0], e.opd[1]);
|
||||
}
|
||||
return op(e.opd[1], e.opd[1], e.opd[0]);
|
||||
};
|
||||
|
||||
var _compare = function(instr, context) {
|
||||
context.cond.a = instr.parsed.opd[1];
|
||||
context.cond.b = instr.parsed.opd[0];
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, type, zero) {
|
||||
instr.conditional(context.cond.a, context.cond.b, type);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var load_bits = function(register, pointer, bits, signed) {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.read_memory(pointer, register, bits, signed);
|
||||
};
|
||||
|
||||
var store_bits = function(register, pointer, bits, signed) {
|
||||
//pointer, register, bits, is_signed
|
||||
return Base.write_memory(pointer, register, bits, signed);
|
||||
};
|
||||
|
||||
var _setf_v850_cond = {
|
||||
'lt': {
|
||||
cond: 'LT',
|
||||
arg1: null,
|
||||
},
|
||||
'le': {
|
||||
cond: 'LE',
|
||||
arg1: null,
|
||||
},
|
||||
'ge': {
|
||||
cond: 'GE',
|
||||
arg1: null,
|
||||
},
|
||||
'gt': {
|
||||
cond: 'GT',
|
||||
arg1: null,
|
||||
},
|
||||
'nh': {
|
||||
cond: 'LE',
|
||||
arg1: null,
|
||||
},
|
||||
'h': {
|
||||
cond: 'GT',
|
||||
arg1: null,
|
||||
},
|
||||
'nz': {
|
||||
cond: 'NE',
|
||||
arg1: '0',
|
||||
},
|
||||
'z': {
|
||||
cond: 'EQ',
|
||||
arg1: '0',
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
instructions: {
|
||||
add: function(instr) {
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
and: function(instr) {
|
||||
return _common_math(instr.parsed, Base.and);
|
||||
},
|
||||
b: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
bne: function(instr, context) {
|
||||
return _conditional(instr, context, 'EQ');
|
||||
},
|
||||
beq: function(instr, context) {
|
||||
return _conditional(instr, context, 'NE');
|
||||
},
|
||||
bgt: function(instr, context) {
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
bnh: function(instr, context) {
|
||||
//branch not higher = LE
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bh: function(instr, context) {
|
||||
//branch higher = GT
|
||||
return _conditional(instr, context, 'LE');
|
||||
},
|
||||
bge: function(instr, context) {
|
||||
return _conditional(instr, context, 'LT');
|
||||
},
|
||||
blt: function(instr, context) {
|
||||
return _conditional(instr, context, 'GE');
|
||||
},
|
||||
ble: function(instr, context) {
|
||||
return _conditional(instr, context, 'GT');
|
||||
},
|
||||
bz: function(instr, context) {
|
||||
return _conditional(instr, context, 'NE');
|
||||
},
|
||||
bnz: function(instr, context) {
|
||||
return _conditional(instr, context, 'EQ');
|
||||
},
|
||||
cmp: _compare,
|
||||
jarl: function(instr, context, instructions) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
instr.comments.push('link pointer is lost. (used r0)');
|
||||
} else {
|
||||
instr.comments.push(instr.parsed.opd[1] + ' = PC + 4;');
|
||||
}
|
||||
var arg = instr.parsed.opd[0];
|
||||
if (instr.parsed.opd[0].indexOf('0x') == 0) {
|
||||
arg = Variable.functionPointer(arg);
|
||||
}
|
||||
return Base.call(arg, []);
|
||||
},
|
||||
jmp: function(instr, context, instructions) {
|
||||
if ((instructions.length - 1) == instructions.indexOf(instr)) {
|
||||
//name, args, is_pointer, returns, bits
|
||||
return Base.call(instr.parsed.opd[0], [], true, 'return');
|
||||
}
|
||||
return Base.nop();
|
||||
},
|
||||
jr: function() {
|
||||
return Base.nop();
|
||||
},
|
||||
'ld.b': function(instr) {
|
||||
// ld.X 0x6c36[r4], r14
|
||||
// ["ld.X","0x6c36","r4","r14"]
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 8, true);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 8, true);
|
||||
},
|
||||
'ld.bu': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 8, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 8, false);
|
||||
},
|
||||
'ld.h': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 16, true);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 16, true);
|
||||
},
|
||||
'ld.hu': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 16, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 16, false);
|
||||
},
|
||||
'ld.w': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 32, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 32, false);
|
||||
},
|
||||
mov: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.assign(instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
movea: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.assign(instr.parsed.opd[2], instr.parsed.opd[0]);
|
||||
}
|
||||
if (instr.parsed.opd[0] == '0') {
|
||||
return Base.assign(instr.parsed.opd[2], instr.parsed.opd[1]);
|
||||
}
|
||||
return Base.add(instr.parsed.opd[2], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
movhi: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var a = instr.parsed.opd[0];
|
||||
if (a.indexOf('0x') != 0) {
|
||||
a = '0x' + parseInt(a).toString(16);
|
||||
}
|
||||
return Base.assign(instr.parsed.opd[1], a + '0000');
|
||||
},
|
||||
mul: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
//value, bits, is_signed, is_pointer, is_memory
|
||||
var n = Variable.local(instr.parsed.opd[0], 32, true);
|
||||
return Base.multiply(instr.parsed.opd[2], instr.parsed.opd[1], n);
|
||||
},
|
||||
mulh: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.multiply(instr.parsed.opd[1], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
mulhi: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.multiply(instr.parsed.opd[2], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
not: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.not(instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
nop: function(instr) {
|
||||
return Base.nop();
|
||||
},
|
||||
or: function(instr) {
|
||||
return _common_math(instr.parsed, Base.or);
|
||||
},
|
||||
ori: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.or(instr.parsed.opd[2], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
satadd: function(instr) {
|
||||
// add with previous carry
|
||||
return _common_math(instr.parsed, Base.add);
|
||||
},
|
||||
satsub: function(instr) {
|
||||
// add with previous carry
|
||||
return _common_math(instr.parsed, Base.subtract, false);
|
||||
},
|
||||
satsubi: function(instr) {
|
||||
// add with previous carry
|
||||
return _common_math(instr.parsed, Base.subtract, true);
|
||||
},
|
||||
satsubr: function(instr) {
|
||||
// add with previous carry
|
||||
return _common_math(instr.parsed, Base.subtract, true);
|
||||
},
|
||||
shl: function(instr) {
|
||||
return _common_math(instr.parsed, Base.shift_left);
|
||||
},
|
||||
shr: function(instr) {
|
||||
//logically shift right
|
||||
return _common_math(instr.parsed, Base.shift_right);
|
||||
},
|
||||
setf: function(instr, context) {
|
||||
var e = instr.parsed;
|
||||
if (e.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
if (e.opd[0] == 'v' || e.opd[0] == 'nv') {
|
||||
var m = new Variable.macro((e.opd[0] == 'nv' ? '!' : '') + 'IS_OVERFLOW(' + context.cond.a + ', ' + context.cond.b + ')');
|
||||
var op = Base.conditional_assign(e.opd[1], m, null, 'CUST', '1', '0');
|
||||
Global.context.addMarcro('#define IS_OVERFLOW(a,b) (((a<0)&&(b<0)&&(a+b>0))||((a>0)&&(b>0)&&(a+b<0)))');
|
||||
return op;
|
||||
}
|
||||
var o = _setf_v850_cond[e.opd[0]];
|
||||
if (!o) {
|
||||
instr.comments.push('unhandled use-case. please report it.');
|
||||
instr.comments.push(instr.assembly);
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.conditional_assign(e.opd[1], context.cond.a, o.arg1 || context.cond.b, o.cond, '1', '0');
|
||||
},
|
||||
'sld.b': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 8, true);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 8, true);
|
||||
},
|
||||
'sld.bu': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 8, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 8, false);
|
||||
},
|
||||
'sld.h': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 16, true);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 16, true);
|
||||
},
|
||||
'sld.hu': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 16, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 16, false);
|
||||
},
|
||||
'sld.w': function(instr) {
|
||||
var addr = instr.parsed.opd[0];
|
||||
var src = instr.parsed.opd[1];
|
||||
var dst = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return load_bits(dst, addr, 32, false);
|
||||
}
|
||||
return load_bits(dst, src + ' + ' + addr, 32, false);
|
||||
},
|
||||
'sst.b': function(instr) {
|
||||
// st.X , r14, 0x6c36[r4]
|
||||
// ["st.X","r14","0x6c36","r4"]
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 8, true);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 8, true);
|
||||
},
|
||||
'sst.bu': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 8, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 8, false);
|
||||
},
|
||||
'sst.h': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 16, true);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 16, true);
|
||||
},
|
||||
'sst.hu': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 16, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 16, false);
|
||||
},
|
||||
'sst.w': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 32, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 32, false);
|
||||
},
|
||||
'st.b': function(instr) {
|
||||
// st.X , r14, 0x6c36[r4]
|
||||
// ["st.X","r14","0x6c36","r4"]
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 8, true);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 8, true);
|
||||
},
|
||||
'st.bu': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 8, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 8, false);
|
||||
},
|
||||
'st.h': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 16, true);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 16, true);
|
||||
},
|
||||
'st.hu': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 16, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 16, false);
|
||||
},
|
||||
'st.w': function(instr) {
|
||||
var dst = instr.parsed.opd[0];
|
||||
var addr = instr.parsed.opd[1];
|
||||
var src = instr.parsed.opd[2];
|
||||
if (src == '0') {
|
||||
return store_bits(dst, addr, 32, false);
|
||||
}
|
||||
return store_bits(dst, src + ' + ' + addr, 32, false);
|
||||
},
|
||||
sub: function(instr) {
|
||||
return _common_math(instr.parsed, Base.subtract);
|
||||
},
|
||||
subr: function(instr) {
|
||||
return _common_math(instr.parsed, Base.subtract, true);
|
||||
},
|
||||
sxb: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.extend(instr.parsed.opd[1], instr.parsed.opd[0], 32);
|
||||
},
|
||||
sxh: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.extend(instr.parsed.opd[1], instr.parsed.opd[0], 32);
|
||||
},
|
||||
tst: function(instr, context, instructions) {
|
||||
var e = instr.parsed;
|
||||
context.cond.a = (e.opd[0] == e.opd[1]) ? e.opd[1] : "(" + e.opd[1] + " & " + e.opd[0] + ")";
|
||||
context.cond.b = '0';
|
||||
return Base.nop();
|
||||
},
|
||||
xor: function(instr) {
|
||||
return _common_math(instr.parsed, Base.xor);
|
||||
},
|
||||
xori: function(instr) {
|
||||
if (instr.parsed.opd[2] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
return Base.xor(instr.parsed.opd[2], instr.parsed.opd[1], instr.parsed.opd[0]);
|
||||
},
|
||||
zxb: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var a = Variable.local(instr.parsed.opd[0], 32, false);
|
||||
return Base.assign(instr.parsed.opd[1], a);
|
||||
},
|
||||
zxh: function(instr) {
|
||||
if (instr.parsed.opd[1] == '0') {
|
||||
return Base.nop();
|
||||
}
|
||||
var a = Variable.local(instr.parsed.opd[0], 32, false);
|
||||
return Base.assign(instr.parsed.opd[1], a);
|
||||
},
|
||||
invalid: function() {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(assembly) {
|
||||
var ret = assembly.replace(/\[|\]/g, ' ').replace(/,/g, ' ');
|
||||
ret = ret.replace(/\{|\}/g, ' ').replace(/\s+/g, ' ');
|
||||
ret = ret.trim().replace(/\br0\b/g, '0').split(' ');
|
||||
|
||||
return {
|
||||
mnem: ret.shift(),
|
||||
opd: ret
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
cond: {
|
||||
a: '?',
|
||||
b: '?'
|
||||
},
|
||||
leave: false,
|
||||
vars: []
|
||||
};
|
||||
},
|
||||
localvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
globalvars: function(context) {
|
||||
return [];
|
||||
},
|
||||
arguments: function(context) {
|
||||
return [];
|
||||
},
|
||||
returns: function(context) {
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,560 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2021 deroad
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
(function() { // lgtm [js/useless-expression]
|
||||
|
||||
var Base = require('libdec/core/base');
|
||||
var Variable = require('libdec/core/variable');
|
||||
var Extra = require('libdec/core/extra');
|
||||
|
||||
var _memory_name = "_memory";
|
||||
|
||||
var _to_argument = function(wasm_var) {
|
||||
return wasm_var.toString('arg');
|
||||
};
|
||||
|
||||
var WasmVar = function(name, type, instr) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.instr = instr;
|
||||
this.toString = function(s) {
|
||||
if (s == 'arg') {
|
||||
return this.type + ' ' + this.name;
|
||||
}
|
||||
return this.name;
|
||||
};
|
||||
};
|
||||
|
||||
var StackVar = function(type, instr) {
|
||||
return new WasmVar(Variable.uniqueName('stack'), type, instr);
|
||||
};
|
||||
|
||||
var _is_next_a_set = function(instr, instructions) {
|
||||
var pos = instructions.indexOf(instr) + 1;
|
||||
if (!instructions[pos]) {
|
||||
return false;
|
||||
}
|
||||
var mnem = instructions[pos].parsed.mnem;
|
||||
return mnem == 'set_local' || mnem == 'set_global' ? pos : -1;
|
||||
};
|
||||
|
||||
var _is_next_local = function(instr, instructions) {
|
||||
var pos = instructions.indexOf(instr) + 1;
|
||||
if (!instructions[pos]) {
|
||||
return false;
|
||||
}
|
||||
var mnem = instructions[pos].parsed.mnem;
|
||||
return mnem == 'set_local' ? pos : -1;
|
||||
};
|
||||
|
||||
var _is_previous_call = function(instr, instructions) {
|
||||
var pos = instructions.indexOf(instr) - 1;
|
||||
if (!instructions[pos]) {
|
||||
return false;
|
||||
}
|
||||
var mnem = instructions[pos].parsed.mnem;
|
||||
return mnem == 'call' ? pos : -1;
|
||||
};
|
||||
|
||||
var _set_local = function(instr, context, instructions, type, allow_args) {
|
||||
var pos = _is_next_local(instr, instructions);
|
||||
var name = allow_args ? 'arg_' : 'local';
|
||||
var n = '';
|
||||
if (pos < 0) {
|
||||
n = instr.parsed.opd[0];
|
||||
if (!context.local[n]) {
|
||||
name += n;
|
||||
context.local[n] = new WasmVar(name, type, instr);
|
||||
if (allow_args) {
|
||||
context.input[n] = context.local[n];
|
||||
}
|
||||
}
|
||||
return context.local[n];
|
||||
}
|
||||
n = instructions[pos].parsed.opd[0];
|
||||
if (!context.local[n]) {
|
||||
name += n;
|
||||
context.local[n] = new WasmVar(name, type, instr);
|
||||
if (allow_args) {
|
||||
context.input[n] = context.local[n];
|
||||
}
|
||||
}
|
||||
return context.local[n];
|
||||
};
|
||||
|
||||
var _set_global = function(instr, context, instructions, type) {
|
||||
var n = instr.parsed.opd[0].trim();
|
||||
if (!context.global[n]) {
|
||||
context.global[n] = new WasmVar('global_' + instr.parsed.opd[0], type, instr);
|
||||
}
|
||||
return context.global[n];
|
||||
};
|
||||
|
||||
var _bits = function(o) {
|
||||
if (typeof o == 'object') {
|
||||
o = o.parsed.type;
|
||||
}
|
||||
switch (o) {
|
||||
case 'f32':
|
||||
return 32;
|
||||
case 'f64':
|
||||
return 64;
|
||||
case 'i64':
|
||||
return 64;
|
||||
default:
|
||||
return 32;
|
||||
}
|
||||
};
|
||||
|
||||
var _type = function(o, unsigned) {
|
||||
if (typeof o == 'object') {
|
||||
o = o.parsed.type;
|
||||
}
|
||||
switch (o) {
|
||||
case 'f32':
|
||||
return 'float';
|
||||
case 'f64':
|
||||
return 'double';
|
||||
case 'i64':
|
||||
return unsigned ? 'uint64_t' : 'int64_t';
|
||||
default:
|
||||
return unsigned ? 'uint32_t' : 'int32_t';
|
||||
}
|
||||
};
|
||||
|
||||
var _remove_const = function(instr, instructions, stackval) {
|
||||
if (stackval && instructions.indexOf(stackval.instr) == (instructions.indexOf(instr) - 1) && stackval.instr.parsed.mnem == 'const') {
|
||||
stackval.instr.valid = false;
|
||||
// <type>.const <N>
|
||||
return stackval.instr.parsed.opd[0];
|
||||
}
|
||||
return stackval;
|
||||
};
|
||||
|
||||
var _math = function(instr, context, instructions, type, op) {
|
||||
var source_b = context.stack.pop();
|
||||
source_b = _remove_const(instr, instructions, source_b);
|
||||
var source_a = context.stack.pop();
|
||||
source_a.type = type;
|
||||
source_b.type = type;
|
||||
var pos = _is_next_a_set(instr, instructions);
|
||||
var destination = StackVar(type, instr);
|
||||
context.stack.push(destination); // push must happen.
|
||||
if (pos < 0) {
|
||||
return op(destination.toString(), source_a.toString(), source_b.toString());
|
||||
}
|
||||
var next_set = instructions[pos];
|
||||
next_set.valid = false;
|
||||
if (next_set.parsed.mnem == 'set_local') {
|
||||
destination = _set_local(next_set, context, instructions, 'int32_t', false);
|
||||
} else {
|
||||
destination = _set_global(next_set, context, instructions, 'int32_t');
|
||||
}
|
||||
return op(destination.toString(), source_a.toString(), source_b.toString());
|
||||
};
|
||||
|
||||
var _cmp = {
|
||||
eq: 'EQ',
|
||||
ne: 'NE',
|
||||
eqz: 'EQ',
|
||||
nez: 'NE',
|
||||
gt_s: 'GT',
|
||||
gt_u: 'GT',
|
||||
ge_s: 'GE',
|
||||
ge_u: 'GE',
|
||||
lt_s: 'LT',
|
||||
lt_u: 'LT',
|
||||
le_s: 'LE',
|
||||
le_u: 'LE',
|
||||
gt: 'GT',
|
||||
ge: 'GE',
|
||||
lt: 'LT',
|
||||
le: 'LE'
|
||||
};
|
||||
|
||||
var _conditional = function(instr, context, instructions) {
|
||||
var cond = {};
|
||||
cond.b = context.stack.pop().toString();
|
||||
cond.a = context.stack.pop().toString();
|
||||
if (!cond.a) {
|
||||
cond.a = '?';
|
||||
}
|
||||
if (!cond.b) {
|
||||
cond.b = '?';
|
||||
}
|
||||
cond.cmp = _cmp[instr.parsed.mnem];
|
||||
context.condstack.push(cond);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _conditional_zero = function(instr, context, instructions) {
|
||||
var cond = {};
|
||||
cond.b = '0';
|
||||
cond.a = context.stack.pop().toString();
|
||||
if (!cond.a) {
|
||||
cond.a = '?';
|
||||
}
|
||||
cond.cmp = _cmp[instr.parsed.mnem];
|
||||
context.condstack.push(cond);
|
||||
return Base.nop();
|
||||
};
|
||||
|
||||
var _set_instruction_conditional = function(instr, context) {
|
||||
var cond = context.condstack.pop();
|
||||
if (!cond) {
|
||||
var arg = context.stack.pop();
|
||||
if (!arg) {
|
||||
arg = '?';
|
||||
}
|
||||
instr.conditional(arg.toString(), '0', 'NE');
|
||||
} else {
|
||||
instr.conditional(cond.a, cond.b, cond.cmp);
|
||||
}
|
||||
};
|
||||
|
||||
var _common_load = function(instr, context) {
|
||||
context.memory = true;
|
||||
var offset = instr.parsed.opd[instr.parsed.opd.length - 1];
|
||||
var pointer = context.stack.pop().toString();
|
||||
var register = StackVar(_type(instr), instr);
|
||||
context.stack.push(register);
|
||||
if (offset != '0') {
|
||||
var bits = instr.parsed.mnem.match(/\d+/) || ["32"];
|
||||
bits = parseInt(bits[0]) / 8;
|
||||
pointer += ' + ' + (parseInt(offset) / bits).toString();
|
||||
}
|
||||
pointer = _memory_name + ' + ' + pointer;
|
||||
return Base.read_memory(pointer, register.toString(), _bits(instr), instr.parsed.mnem.endsWith('_s'));
|
||||
};
|
||||
|
||||
var _common_store = function(instr, context) {
|
||||
context.memory = true;
|
||||
var offset = instr.parsed.opd[instr.parsed.opd.length - 1];
|
||||
var register = context.stack.pop().toString();
|
||||
var pointer = context.stack.pop().toString();
|
||||
var bits = instr.parsed.mnem.match(/\d+/) || ["32"];
|
||||
bits = parseInt(bits[0]);
|
||||
if (offset != '0') {
|
||||
pointer += ' + ' + (parseInt(offset) / (bits / 8)).toString();
|
||||
}
|
||||
pointer = _memory_name + ' + ' + pointer;
|
||||
return Base.write_memory(pointer, register.toString(), bits, false);
|
||||
};
|
||||
|
||||
var _wasm_arch = {
|
||||
instructions: {
|
||||
eq: _conditional,
|
||||
ne: _conditional,
|
||||
eqz: _conditional_zero,
|
||||
nez: _conditional_zero,
|
||||
gt_s: _conditional,
|
||||
gt_u: _conditional,
|
||||
ge_s: _conditional,
|
||||
ge_u: _conditional,
|
||||
lt_s: _conditional,
|
||||
lt_u: _conditional,
|
||||
le_s: _conditional,
|
||||
le_u: _conditional,
|
||||
gt: _conditional,
|
||||
ge: _conditional,
|
||||
lt: _conditional,
|
||||
le: _conditional,
|
||||
const: function(instr, context, instructions) {
|
||||
var s = StackVar('const ' + _type(instr), instr);
|
||||
context.stack.push(s);
|
||||
var num = parseInt(instr.parsed.opd[0]);
|
||||
if (num > 1023) {
|
||||
num = '0x' + num.toString(16);
|
||||
}
|
||||
return Base.assign(s.toString(), num.toString());
|
||||
},
|
||||
load: _common_load,
|
||||
load8_s: _common_load,
|
||||
load8_u: _common_load,
|
||||
load16_s: _common_load,
|
||||
load16_u: _common_load,
|
||||
store: _common_store,
|
||||
store8: _common_store,
|
||||
store16: _common_store,
|
||||
store32: _common_store,
|
||||
add: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.add);
|
||||
},
|
||||
and: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.and);
|
||||
},
|
||||
div_s: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.divide);
|
||||
},
|
||||
div_u: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr, true), Base.divide);
|
||||
},
|
||||
extend_s: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr));
|
||||
},
|
||||
extend_u: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr, true));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr, true));
|
||||
},
|
||||
mul: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.multiply);
|
||||
},
|
||||
or: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.or);
|
||||
},
|
||||
reinterpret: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.assign(s.toString('arg'), b.toString());
|
||||
}
|
||||
return Base.assign(s.toString(), b.toString());
|
||||
},
|
||||
rem_s: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.module);
|
||||
},
|
||||
rem_u: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr, true), Base.module);
|
||||
},
|
||||
shl: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.shift_left);
|
||||
},
|
||||
shr_s: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.shift_right);
|
||||
},
|
||||
shr_u: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr, true), Base.shift_right);
|
||||
},
|
||||
sub: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.subtract);
|
||||
},
|
||||
wrap: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr, true));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr, true));
|
||||
},
|
||||
trunc_s: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr, true));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr, true));
|
||||
},
|
||||
trunc_u: function(instr, context, instructions) {
|
||||
var s = _set_local(instr, context, instructions, _type(instr), false);
|
||||
var b = context.stack.pop();
|
||||
context.stack.push(s);
|
||||
if (_is_next_local(instr, instructions) < 0) {
|
||||
return Base.extend(s.toString('arg'), b.toString(), _type(instr, true));
|
||||
}
|
||||
return Base.extend(s.toString(), b.toString(), _type(instr, true));
|
||||
},
|
||||
xor: function(instr, context, instructions) {
|
||||
return _math(instr, context, instructions, _type(instr), Base.xor);
|
||||
},
|
||||
get_global: function(instr, context, instructions) {
|
||||
var dst = _set_global(instr, context, instructions, 'int32_t');
|
||||
context.stack.push(dst);
|
||||
return Base.nop();
|
||||
},
|
||||
set_global: function(instr, context, instructions) {
|
||||
var dst = _set_global(instr, context, instructions, 'int32_t');
|
||||
var arg = context.stack.pop();
|
||||
return Base.assign(dst.toString(), arg ? arg.toString() : "?");
|
||||
},
|
||||
get_local: function(instr, context, instructions) {
|
||||
context.stack.push(_set_local(instr, context, instructions, 'int32_t', true));
|
||||
return Base.nop();
|
||||
},
|
||||
set_local: function(instr, context, instructions) {
|
||||
var dst = _set_local(instr, context, instructions, 'int32_t', false);
|
||||
var pos = _is_previous_call(instr, instructions);
|
||||
if (pos > -1) {
|
||||
var call = instructions[pos];
|
||||
call.code = Base.assign(dst.toString(), call.code);
|
||||
return Base.nop();
|
||||
}
|
||||
var arg = context.stack.pop();
|
||||
return Base.assign(dst.toString(), arg ? arg.toString() : "?");
|
||||
},
|
||||
tee_local: function(instr, context, instructions) {
|
||||
var dst = _set_local(instr, context, instructions, 'int32_t', true);
|
||||
var pos = _is_previous_call(instr, instructions);
|
||||
if (pos > -1) {
|
||||
var call = instructions[pos];
|
||||
call.code = Base.assign(dst.toString(), call.code);
|
||||
return Base.nop();
|
||||
}
|
||||
var arg = context.stack[context.stack.length - 1];
|
||||
return Base.assign(dst.toString(), arg.toString());
|
||||
},
|
||||
drop: function(instr, context, instructions) {
|
||||
_remove_const(instr, instructions, context.stack.pop());
|
||||
return Base.nop();
|
||||
},
|
||||
call: function(instr, context, instructions) {
|
||||
var args = [];
|
||||
for (var i = instructions.indexOf(instr) - 1; i >= 0; i--) {
|
||||
var previous = instructions[i].parsed.mnem;
|
||||
if (!previous.startsWith('get_') && previous != 'const') {
|
||||
break;
|
||||
}
|
||||
args.unshift(context.stack.pop());
|
||||
}
|
||||
if (instr.parsed.opd[0].match(/^\d+$/)) {
|
||||
return Base.call('fcn_' + instr.parsed.opd[0], args);
|
||||
}
|
||||
return Base.call(instr.parsed.opd[0], args);
|
||||
},
|
||||
return: function(instr, context, instructions) {
|
||||
var ret = null;
|
||||
if (context.stack.length > 0) {
|
||||
if (context.stack.length > 1) {
|
||||
Global.warning('[wasm] stack len is not zero: ' + context.stack.length);
|
||||
}
|
||||
context.returned = context.stack.pop();
|
||||
ret = _remove_const(instr, instructions, context.returned);
|
||||
if (!Extra.is.string(ret)) {
|
||||
ret = context.returned.toString();
|
||||
}
|
||||
}
|
||||
return Base.return(ret);
|
||||
},
|
||||
'if': function(instr, context, instructions) {
|
||||
_set_instruction_conditional(instr, context);
|
||||
return Base.nop();
|
||||
},
|
||||
br_if: function(instr, context, instructions) {
|
||||
_set_instruction_conditional(instr, context);
|
||||
return Base.nop();
|
||||
},
|
||||
'else': function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
block: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
loop: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
end: function(instr, context, instructions) {
|
||||
if (instr.jump && instr.jump.lte(instr.location)) {
|
||||
_set_instruction_conditional(instr, context);
|
||||
} else {
|
||||
var curidx = instructions.indexOf(instr);
|
||||
if (curidx == (instructions.length - 1)) {
|
||||
var ret = null;
|
||||
if (context.stack.length > 0) {
|
||||
if (context.stack.length > 1) {
|
||||
Global.warning('[wasm] stack len is not zero: ' + context.stack.length);
|
||||
}
|
||||
context.returned = context.stack.pop();
|
||||
ret = _remove_const(instr, instructions, context.returned);
|
||||
if (!Extra.is.string(ret)) {
|
||||
ret = ret.toString();
|
||||
}
|
||||
}
|
||||
return Base.return(ret);
|
||||
}
|
||||
}
|
||||
|
||||
return Base.nop();
|
||||
},
|
||||
br: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
nop: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
},
|
||||
invalid: function(instr, context, instructions) {
|
||||
return Base.nop();
|
||||
}
|
||||
},
|
||||
parse: function(asm) {
|
||||
asm = asm.trim().replace(/\s+/g, ' ').replace(/\//g, ' ');
|
||||
var type = asm.match(/[if]\d\d\./);
|
||||
if (type) {
|
||||
type = type[0];
|
||||
asm = asm.replace(/[if]\d\d\./, '');
|
||||
}
|
||||
asm = asm.split(' ');
|
||||
|
||||
return {
|
||||
type: type || 'i32',
|
||||
mnem: asm.shift(),
|
||||
opd: asm
|
||||
};
|
||||
},
|
||||
context: function() {
|
||||
return {
|
||||
returned: null,
|
||||
global: {},
|
||||
input: {},
|
||||
local: {},
|
||||
stack: [],
|
||||
condstack: [],
|
||||
memory: false,
|
||||
};
|
||||
},
|
||||
globalvars: function(context) {
|
||||
var to_return = Extra.to.array(context.global).map(_to_argument);
|
||||
if (context.memory) {
|
||||
to_return.unshift("extern uint8_t " + _memory_name + "[]");
|
||||
}
|
||||
return to_return;
|
||||
},
|
||||
localvars: function(context) {
|
||||
var vars = [];
|
||||
for (var k in context.local) {
|
||||
var o = context.local[k];
|
||||
if (Extra.is.inObject(context.input, o) || Extra.is.inObject(context.global, o)) {
|
||||
continue;
|
||||
}
|
||||
vars.push(o.toString('arg'));
|
||||
}
|
||||
return vars;
|
||||
},
|
||||
arguments: function(context) {
|
||||
return Extra.to.array(context.input).map(_to_argument);
|
||||
},
|
||||
returns: function(context) {
|
||||
if (context.returned) {
|
||||
return context.returned.type.replace(/const\s/, '');
|
||||
}
|
||||
return 'void';
|
||||
}
|
||||
};
|
||||
return _wasm_arch;
|
||||
});
|
||||
Loaded 100 of 166 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user