diff --git a/.dockerignore b/.dockerignore index 69ca821..130b1e4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,16 @@ +.git/ +venv/ +tmp/ + build/ +**/build/ +**/build-*/ +**/build*/ + +**/subprojects/.wraplock +**/subprojects/packagecache/ +**/subprojects/approvaltests/ + +**/*.received.* + Dockerfile diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..05bee5d --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,85 @@ +name: build and test + +on: [pull_request] + +jobs: + test-compiler: + name: Test Compiler + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y bison flex libfl-dev g++-13 python3-venv + g++-13 --version + python3 -m venv venv + . venv/bin/activate + python -m pip install --upgrade pip + python -m pip install 'meson>=1.10.1' 'ninja>=1.11' 'reoxide==0.7.2' + + - name: Test compiler + working-directory: src/compiler + run: | + . ../../venv/bin/activate + export XDG_CONFIG_HOME="${RUNNER_TEMP}/config" + mkdir -p "${XDG_CONFIG_HOME}/reoxide" + touch "${XDG_CONFIG_HOME}/reoxide/reoxide.toml" + CXX=g++-13 meson setup build --buildtype release -Dtests=true + meson compile -j 1 -C build + meson test -C build --print-errorlogs + + build-plugin: + name: Build Plugin + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Build plugin + run: docker build -o tmp/out . + + test-plugin: + name: Test Plugin + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + + - name: Install host dependencies + run: | + sudo apt-get update + sudo apt-get install -y bison flex libfl-dev g++-13 python3-venv + python3 -m venv venv + . venv/bin/activate + python -m pip install --upgrade pip + python -m pip install 'meson>=1.10.1' 'ninja>=1.11' 'reoxide==0.7.2' + + - name: Build compiler for rule fixtures + working-directory: src/compiler + run: | + . ../../venv/bin/activate + export XDG_CONFIG_HOME="${RUNNER_TEMP}/config" + mkdir -p "${XDG_CONFIG_HOME}/reoxide" + touch "${XDG_CONFIG_HOME}/reoxide/reoxide.toml" + CXX=g++-13 meson setup build --buildtype release -Dtests=false + meson compile -j 1 -C build + + - name: Build plugin test base image + run: | + docker build \ + -f src/plugin/tests/docker/base.Dockerfile \ + -t pcode-weaver-plugin-test-base:ghidra-12.0_reoxide-0.7.2 \ + . + + - name: Build plugin test image + run: docker build --target plugin-test -t pcode-weaver-plugin-test . + + - name: Test plugin + working-directory: src/plugin/tests + run: | + . ../../../venv/bin/activate + meson setup build --buildtype release + meson test -C build --print-errorlogs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index e3e1840..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: build - -on: [pull_request] - -jobs: - build-compiler: - name: Build Compiler - runs-on: ubuntu-24.04 - - steps: - - uses: actions/checkout@v4 - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y bison flex libfl-dev g++-13 python3-venv - g++-13 --version - python3 -m venv venv - . venv/bin/activate - python -m pip install --upgrade pip - python -m pip install 'meson>=1.10.1' 'ninja>=1.11' 'reoxide==0.7.2' - - - name: Build compiler - working-directory: src/compiler - run: | - . ../../venv/bin/activate - export XDG_CONFIG_HOME="${RUNNER_TEMP}/config" - mkdir -p "${XDG_CONFIG_HOME}/reoxide" - touch "${XDG_CONFIG_HOME}/reoxide/reoxide.toml" - CXX=g++-13 meson setup build --buildtype release - meson compile -j 1 -C build - - build-plugin: - name: Build Plugin - runs-on: ubuntu-22.04 - - steps: - - uses: actions/checkout@v4 - - - name: Build plugin - run: docker build -o tmp/out . diff --git a/.gitignore b/.gitignore index 94a5ed8..8364f20 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,13 @@ src/publisher/parse/parser.cc src/publisher/parse/location.hh .codex tmp -AGENTS.md \ No newline at end of file +AGENTS.md +src/compiler/subprojects/.wraplock +src/compiler/subprojects/approvaltests/ +src/compiler/subprojects/packagecache/ +src/compiler/tests/**/*.received.* + +src/plugin/tests/subprojects/.wraplock +src/plugin/tests/subprojects/approvaltests/ +src/plugin/tests/subprojects/packagecache/ +src/plugin/tests/fixtures/**/*.received.* diff --git a/Dockerfile b/Dockerfile index a2a3add..4c777d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,6 @@ -FROM quay.io/pypa/manylinux2014 AS build +ARG PLUGIN_TEST_BASE_IMAGE=pcode-weaver-plugin-test-base:ghidra-12.0_reoxide-0.7.2 + +FROM quay.io/pypa/manylinux2014 AS plugin-build ARG REOXIDE_VERSION=0.7.2 ENV VIRTUAL_ENV=/venv @@ -17,12 +19,27 @@ RUN --mount=type=cache,target=/root/.cache/pip \ touch /root/.config/reoxide/reoxide.toml WORKDIR /plugin -COPY . . +COPY src/inc/ ./src/inc/ +COPY src/plugin/meson.build ./src/plugin/meson.build +COPY src/plugin/main/ ./src/plugin/main/ RUN set -ex;\ meson setup build ./src/plugin/ --buildtype release;\ meson install -C build +FROM ${PLUGIN_TEST_BASE_IMAGE} AS plugin-test + +COPY --from=plugin-build /root/.local/share/reoxide/plugins/*.so /opt/reoxide/data/plugins/ +COPY src/plugin/tests/scripts/ /opt/pcode-weaver/scripts/ +COPY scripts/install-pcodeweaver-action.sh /opt/pcode-weaver/scripts/install-pcodeweaver-action.sh + +RUN set -ex;\ + default_reoxide_yaml="$(python3 -c 'from pathlib import Path; import reoxide; print(Path(reoxide.__file__).parent / "data" / "default.yaml")')";\ + cp "${default_reoxide_yaml}" /opt/reoxide/data/current.yaml;\ + sh /opt/pcode-weaver/scripts/install-pcodeweaver-action.sh /opt/reoxide/data/current.yaml;\ + chmod +x /opt/pcode-weaver/scripts/run-case.sh \ + /opt/pcode-weaver/scripts/install-pcodeweaver-action.sh + FROM scratch -COPY --from=build /root/.local/share/reoxide/plugins/*.so . +COPY --from=plugin-build /root/.local/share/reoxide/plugins/*.so . diff --git a/README.md b/README.md index 26183eb..8fd08ed 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Pcode Weaver +[![build and test](https://github.com/osogi/pcode-weaver/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/osogi/pcode-weaver/actions/workflows/build-and-test.yml) +[![formatting-check](https://github.com/osogi/pcode-weaver/actions/workflows/formatting-check.yml/badge.svg)](https://github.com/osogi/pcode-weaver/actions/workflows/formatting-check.yml) + Experimental ReOxide(Ghidra) plugin for applying custom p-code rewrite rules with validation checks. diff --git a/src/compiler/meson.build b/src/compiler/meson.build index 2430a45..b1eaaab 100644 --- a/src/compiler/meson.build +++ b/src/compiler/meson.build @@ -37,3 +37,7 @@ subdir('validate') subdir('rulecompile') subdir('bin') + +if get_option('tests') + subdir('tests') +endif diff --git a/src/compiler/meson_options.txt b/src/compiler/meson_options.txt new file mode 100644 index 0000000..c849cea --- /dev/null +++ b/src/compiler/meson_options.txt @@ -0,0 +1 @@ +option('tests', type: 'boolean', value: false, description: 'Build compiler unit tests') diff --git a/src/compiler/parse/context.hh b/src/compiler/parse/context.hh index 24ade91..199c425 100644 --- a/src/compiler/parse/context.hh +++ b/src/compiler/parse/context.hh @@ -7,7 +7,7 @@ #include "parse/op_type_predefined.hh" // generated headers -#include "parse/parser.hh" +#include "parse/parser.hpp" struct Context { Context() diff --git a/src/compiler/parse/driver.hh b/src/compiler/parse/driver.hh index b713773..eef7837 100644 --- a/src/compiler/parse/driver.hh +++ b/src/compiler/parse/driver.hh @@ -8,7 +8,7 @@ #include "parse/scanner.hh" // generated headers -#include "parse/parser.hh" +#include "parse/parser.hpp" #include diff --git a/src/compiler/parse/op_type_predefined.cpp b/src/compiler/parse/op_type_predefined.cpp index a7b72b2..0d6a5c3 100644 --- a/src/compiler/parse/op_type_predefined.cpp +++ b/src/compiler/parse/op_type_predefined.cpp @@ -4,7 +4,7 @@ #include "parse/op_type_predefined.hh" // generated headers -#include "parse/parser.hh" +#include "parse/parser.hpp" #include #include diff --git a/src/compiler/parse/parser.yy b/src/compiler/parse/parser.yy index 0e65a46..5467017 100644 --- a/src/compiler/parse/parser.yy +++ b/src/compiler/parse/parser.yy @@ -8,7 +8,7 @@ %skeleton "lalr1.cc" %require "3.8" -%defines "parse/parser.hh" +%defines "parse/parser.hpp" %define api.parser.class { Parser } %define api.token.constructor @@ -37,7 +37,7 @@ #include "parse/driver.hh" #include "parse/scanner.hh" - #include "parse/parser.hh" + #include "parse/parser.hpp" static yy::Parser::symbol_type yylex(yy::Scanner &scanner) { return scanner.get_next_token(); diff --git a/src/compiler/parse/scanner.hh b/src/compiler/parse/scanner.hh index 8057d7a..26ebe9c 100644 --- a/src/compiler/parse/scanner.hh +++ b/src/compiler/parse/scanner.hh @@ -8,7 +8,7 @@ #pragma once // generated headers -#include "parse/parser.hh" +#include "parse/parser.hpp" #undef yyFlexLexer #include diff --git a/src/compiler/parse/scanner.ll b/src/compiler/parse/scanner.ll index cff3570..37029f3 100644 --- a/src/compiler/parse/scanner.ll +++ b/src/compiler/parse/scanner.ll @@ -17,7 +17,7 @@ #include "parse/driver.hh" #include "parse/scanner.hh" - #include "parse/parser.hh" + #include "parse/parser.hpp" // Original yyterminate() macro returns int. Since we're using Bison 3 variants // as tokens, we must redefine it to change type from `int` to `Parser::semantic_type` diff --git a/src/compiler/subprojects/approvaltests.wrap b/src/compiler/subprojects/approvaltests.wrap new file mode 100644 index 0000000..13e345a --- /dev/null +++ b/src/compiler/subprojects/approvaltests.wrap @@ -0,0 +1,9 @@ +[wrap-git] +url = https://github.com/approvals/ApprovalTests.cpp.git +revision = v.10.13.0 +depth = 1 +method = meson +patch_directory = approvaltests + +[provide] +approvaltests = approvaltests_dep diff --git a/src/compiler/subprojects/packagefiles/approvaltests/meson.build b/src/compiler/subprojects/packagefiles/approvaltests/meson.build new file mode 100644 index 0000000..68ed4d4 --- /dev/null +++ b/src/compiler/subprojects/packagefiles/approvaltests/meson.build @@ -0,0 +1,31 @@ +project( + 'approvaltests', + 'cpp', + version: '10.13.0', +) + +approvaltests_sources = run_command( + 'find', + 'ApprovalTests', + '-name', + '*.cpp', + check: true, +).stdout().strip().split('\n') + +approvaltests_inc = [ + include_directories('.'), + include_directories('ApprovalTests'), +] + +approvaltests_lib = static_library( + 'approvaltests', + approvaltests_sources, + include_directories: approvaltests_inc, +) + +approvaltests_dep = declare_dependency( + include_directories: approvaltests_inc, + link_with: approvaltests_lib, +) + +meson.override_dependency('approvaltests', approvaltests_dep) diff --git a/src/compiler/tests/README.md b/src/compiler/tests/README.md new file mode 100644 index 0000000..db9d6b7 --- /dev/null +++ b/src/compiler/tests/README.md @@ -0,0 +1,30 @@ +# Compiler Tests + +Compiler tests are disabled by default. Golden tests use ApprovalTests.cpp for +approved/received output comparison, and Meson runs one fixture per test. + +To build and run the tests: + +```sh +cd src/compiler +# Or any other C++23 compiler +CXX=g++-13 meson setup build-tests -Dtests=true +meson compile -j 1 -C build-tests +meson test -C build-tests +``` + +Golden tests are discovered from these fixture directories when the build +directory is configured: + +```text +src/compiler/tests/fixtures/parser/ +src/compiler/tests/fixtures/validate/ +src/compiler/tests/fixtures/compile/ +``` + +Each `.rule` fixture should have a sibling `.approved.txt` file. If you add or +remove a `.rule` fixture, reconfigure the test build: + +```sh +meson setup --reconfigure build-tests +``` diff --git a/src/compiler/tests/fixtures/parser/copy_propagation.approved.txt b/src/compiler/tests/fixtures/parser/copy_propagation.approved.txt new file mode 100644 index 0000000..5519344 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/copy_propagation.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/parser/copy_propagation.rule +parse_status: 0 +stderr: + +ast: +v_orig ->(0) o_copy(COPY, __b_0) -> v_copy ->(0) o_any(ANY, __b_1) +-- +v_orig ->>(0) o_any + diff --git a/src/compiler/tests/fixtures/parser/copy_propagation.rule b/src/compiler/tests/fixtures/parser/copy_propagation.rule new file mode 100644 index 0000000..16cf296 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/copy_propagation.rule @@ -0,0 +1,3 @@ +v_orig -> (0) o_copy(COPY, _) -> v_copy -> (0) o_any(_, _) +-- +v_orig ->> (0) o_any diff --git a/src/compiler/tests/fixtures/parser/empty_examples.approved.txt b/src/compiler/tests/fixtures/parser/empty_examples.approved.txt new file mode 100644 index 0000000..758e022 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/empty_examples.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/parser/empty_examples.rule +parse_status: 0 +stderr: + +ast: +VEMPTY ->(0) OPEMPTY -> VEMPTY +-- +VEMPTY ->>(0) op_new + diff --git a/src/compiler/tests/fixtures/parser/empty_examples.rule b/src/compiler/tests/fixtures/parser/empty_examples.rule new file mode 100644 index 0000000..988bebe --- /dev/null +++ b/src/compiler/tests/fixtures/parser/empty_examples.rule @@ -0,0 +1,3 @@ +VEMPTY -> (0) OPEMPTY -> VEMPTY +-- +VEMPTY ->>(0) op_new \ No newline at end of file diff --git a/src/compiler/tests/fixtures/parser/invalid_arrow_action.approved.txt b/src/compiler/tests/fixtures/parser/invalid_arrow_action.approved.txt new file mode 100644 index 0000000..978b068 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_arrow_action.approved.txt @@ -0,0 +1,5 @@ +fixture: fixtures/parser/invalid_arrow_action.rule +parse_status: 1 +stderr: +Parser error: fixtures/parser/invalid_arrow_action.rule:3:4: syntax error, unexpected ->, expecting end of file + diff --git a/src/compiler/tests/fixtures/parser/invalid_arrow_action.rule b/src/compiler/tests/fixtures/parser/invalid_arrow_action.rule new file mode 100644 index 0000000..822d2f5 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_arrow_action.rule @@ -0,0 +1,3 @@ +v1 -> (0) op(INT_ADD, _) +-- +v1 -> (0) op diff --git a/src/compiler/tests/fixtures/parser/invalid_arrow_pattern copy.approved.txt b/src/compiler/tests/fixtures/parser/invalid_arrow_pattern copy.approved.txt new file mode 100644 index 0000000..4479b6d --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_arrow_pattern copy.approved.txt @@ -0,0 +1,5 @@ +fixture: fixtures/parser/invalid_arrow_pattern copy.rule +parse_status: 1 +stderr: +Parser error: fixtures/parser/invalid_arrow_pattern copy.rule:1:4: syntax error, unexpected ->>, expecting -- or ; + diff --git a/src/compiler/tests/fixtures/parser/invalid_arrow_pattern copy.rule b/src/compiler/tests/fixtures/parser/invalid_arrow_pattern copy.rule new file mode 100644 index 0000000..a9d32ec --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_arrow_pattern copy.rule @@ -0,0 +1,3 @@ +v1 ->> (0) op(INT_ADD, _) +-- +v1 ->> (0) op diff --git a/src/compiler/tests/fixtures/parser/invalid_missing_action_separator.approved.txt b/src/compiler/tests/fixtures/parser/invalid_missing_action_separator.approved.txt new file mode 100644 index 0000000..a7376ed --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_missing_action_separator.approved.txt @@ -0,0 +1,5 @@ +fixture: fixtures/parser/invalid_missing_action_separator.rule +parse_status: 1 +stderr: +Parser error: fixtures/parser/invalid_missing_action_separator.rule:2:1: syntax error, unexpected varnode name, expecting -- or ; + diff --git a/src/compiler/tests/fixtures/parser/invalid_missing_action_separator.rule b/src/compiler/tests/fixtures/parser/invalid_missing_action_separator.rule new file mode 100644 index 0000000..9dc4bcd --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_missing_action_separator.rule @@ -0,0 +1,2 @@ +v_orig -> (0) o_copy(COPY, _) +v_orig ->> (0) o_copy diff --git a/src/compiler/tests/fixtures/parser/invalid_trailing_pattern_separator.approved.txt b/src/compiler/tests/fixtures/parser/invalid_trailing_pattern_separator.approved.txt new file mode 100644 index 0000000..ef7f1b0 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_trailing_pattern_separator.approved.txt @@ -0,0 +1,5 @@ +fixture: fixtures/parser/invalid_trailing_pattern_separator.rule +parse_status: 1 +stderr: +Parser error: fixtures/parser/invalid_trailing_pattern_separator.rule:2:1: syntax error, unexpected -- + diff --git a/src/compiler/tests/fixtures/parser/invalid_trailing_pattern_separator.rule b/src/compiler/tests/fixtures/parser/invalid_trailing_pattern_separator.rule new file mode 100644 index 0000000..1d5e9a9 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_trailing_pattern_separator.rule @@ -0,0 +1,3 @@ +v_orig; +-- +v_orig diff --git a/src/compiler/tests/fixtures/parser/invalid_unknown_opcode.approved.txt b/src/compiler/tests/fixtures/parser/invalid_unknown_opcode.approved.txt new file mode 100644 index 0000000..001d8a7 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_unknown_opcode.approved.txt @@ -0,0 +1,5 @@ +fixture: fixtures/parser/invalid_unknown_opcode.rule +parse_status: 1 +stderr: +Parser error: fixtures/parser/invalid_unknown_opcode.rule:1:7: syntax error, unexpected variable name + diff --git a/src/compiler/tests/fixtures/parser/invalid_unknown_opcode.rule b/src/compiler/tests/fixtures/parser/invalid_unknown_opcode.rule new file mode 100644 index 0000000..7b4b062 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_unknown_opcode.rule @@ -0,0 +1,3 @@ +o_bad(INT_UNKNOWN, _) +-- +DELETE o_bad diff --git a/src/compiler/tests/fixtures/parser/invalid_varnode_as_pnode.approved.txt b/src/compiler/tests/fixtures/parser/invalid_varnode_as_pnode.approved.txt new file mode 100644 index 0000000..3b97ad0 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_varnode_as_pnode.approved.txt @@ -0,0 +1,5 @@ +fixture: fixtures/parser/invalid_varnode_as_pnode.rule +parse_status: 1 +stderr: +Parser error: fixtures/parser/invalid_varnode_as_pnode.rule:2:11: syntax error, unexpected varnode name, expecting OPEMPTY or pnode name + diff --git a/src/compiler/tests/fixtures/parser/invalid_varnode_as_pnode.rule b/src/compiler/tests/fixtures/parser/invalid_varnode_as_pnode.rule new file mode 100644 index 0000000..adea23c --- /dev/null +++ b/src/compiler/tests/fixtures/parser/invalid_varnode_as_pnode.rule @@ -0,0 +1,4 @@ +v2 -> (0) op; +v1 -> (0) v2 +-- +v1 -> (0) op diff --git a/src/compiler/tests/fixtures/parser/typed_terms_and_cfg.approved.txt b/src/compiler/tests/fixtures/parser/typed_terms_and_cfg.approved.txt new file mode 100644 index 0000000..b48aecc --- /dev/null +++ b/src/compiler/tests/fixtures/parser/typed_terms_and_cfg.approved.txt @@ -0,0 +1,14 @@ +fixture: fixtures/parser/typed_terms_and_cfg.rule +parse_status: 0 +stderr: + +ast: +o_add(INT_ADD, bb_entry) -> v_sum(8, bb_entry, 16); +bb_entry ->(0) bb_next; +bb_entry <= bb_next +-- +o_new(INT_MULT BEFORE o_add); +#42 ->>(1) o_new; +o_new ->> v_sum; +DELETE o_add + diff --git a/src/compiler/tests/fixtures/parser/typed_terms_and_cfg.rule b/src/compiler/tests/fixtures/parser/typed_terms_and_cfg.rule new file mode 100644 index 0000000..59ade0a --- /dev/null +++ b/src/compiler/tests/fixtures/parser/typed_terms_and_cfg.rule @@ -0,0 +1,8 @@ +o_add(INT_ADD, bb_entry) -> v_sum(8, bb_entry, 16); +bb_entry -> (0) bb_next; +bb_entry <= bb_next +-- +o_new(INT_MULT BEFORE o_add); +#42 ->> (1) o_new; +o_new ->> v_sum; +DELETE o_add diff --git a/src/compiler/tests/fixtures/parser/varnode_offset.approved.txt b/src/compiler/tests/fixtures/parser/varnode_offset.approved.txt new file mode 100644 index 0000000..92fb61d --- /dev/null +++ b/src/compiler/tests/fixtures/parser/varnode_offset.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/parser/varnode_offset.rule +parse_status: 0 +stderr: + +ast: +v1(__s_0, __b_0, 16) ->(0) op(INT_ADD, __b_1) +-- +v1 ->>(0) op + diff --git a/src/compiler/tests/fixtures/parser/varnode_offset.rule b/src/compiler/tests/fixtures/parser/varnode_offset.rule new file mode 100644 index 0000000..41ea986 --- /dev/null +++ b/src/compiler/tests/fixtures/parser/varnode_offset.rule @@ -0,0 +1,3 @@ +v1(_, _, 16) -> (0) op(INT_ADD, _) +-- +v1 ->> (0) op diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_demo_obfuscated.approved.txt b/src/compiler/tests/fixtures/rulecompile/exmp_demo_obfuscated.approved.txt new file mode 100644 index 0000000..039a773 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_demo_obfuscated.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/rulecompile/exmp_demo_obfuscated.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 0 +pattern_steps_count: 11 +action_steps_count: 2 + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_demo_obfuscated.rule b/src/compiler/tests/fixtures/rulecompile/exmp_demo_obfuscated.rule new file mode 100644 index 0000000..cde8309 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_demo_obfuscated.rule @@ -0,0 +1,16 @@ +o_mul (INT_MULT, _); +o_add (INT_ADD, _); +o_and (INT_AND, _); +o_xor (INT_XOR, _); + +v_param -> (0) o_and; +#1 -> (1) o_and -> v_and ->(0) o_mul; + #2 ->(1) o_mul -> v_mul ->(0) o_add; + +v_param -> (0) o_xor -> v_xor ->(1) o_add; +#1 -> (1) o_xor + +-- +v_param ->> (0) o_add; +#1 ->> (1) o_add + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_mask.approved.txt b/src/compiler/tests/fixtures/rulecompile/exmp_mask.approved.txt new file mode 100644 index 0000000..f045f67 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_mask.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/rulecompile/exmp_mask.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 0 +pattern_steps_count: 7 +action_steps_count: 5 + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_mask.rule b/src/compiler/tests/fixtures/rulecompile/exmp_mask.rule new file mode 100644 index 0000000..bb9d4e7 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_mask.rule @@ -0,0 +1,8 @@ +#1 -> (0) o_lft(INT_LEFT, _) -> v_res; +#121 ->(0) o_shift(USERDEFINED, _) -> v_shift_res -> (1)o_lft; +v_bit ->(1) o_shift + +-- +#117 ->>(0) o_mask(USERDEFINED BEFORE o_lft) ->> v_res; +v_bit ->>(1) o_mask; +DELETE o_lft diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_memset.approved.txt b/src/compiler/tests/fixtures/rulecompile/exmp_memset.approved.txt new file mode 100644 index 0000000..c80e956 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_memset.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/rulecompile/exmp_memset.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 0 +pattern_steps_count: 30 +action_steps_count: 9 + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_memset.rule b/src/compiler/tests/fixtures/rulecompile/exmp_memset.rule new file mode 100644 index 0000000..f792cdb --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_memset.rule @@ -0,0 +1,35 @@ +v_flag_0(1, _) ->(0) o_extend_flag(INT_ZEXT, bb_body) -> v_always_0; + +v_always_0 ->(0) o_mult_zero(INT_MULT, _); +#-2 ->(1) o_mult_zero -> vtmp_alw0 ->(0) o_tmp_add(INT_ADD, _); + #1 ->(1) o_tmp_add -> vtmp1 ->(1) o_ptr_add(_,_); + +v_elem_size ->(2) o_ptr_add; +v_cur1 ->(0) o_ptr_add -> v_curtmp ->(0) o_copytmp(COPY, _) -> v_cur2; + +v_orig_prt ->(0) o_cpy1(COPY, _) -> v_cur_tmp ->(0) o_get_cur -> v_cur1; + v_cur2 ->(1) o_get_cur; + +v_cur1 ->(1) o_store(STORE, _); +v_init_val ->(2) o_store; + +#0 -> (1) o_cond(INT_NOTEQUAL, _) -> vtmp2 -> (1) o_loop(CBRANCH, bb_precond); +v_elem_cnt ->(0) o_cpy(COPY, _) -> v_cnt_orig ->(0) o_cnt -> v_cnt ->(0) o_cond; + +bb_body ->(0) bb_precond; +bb_precond ->(1) bb_body + +-- +O_usercall(USERDEFINED BEFORE o_cpy); + +#76 ->>(0) O_usercall; +v_orig_prt ->>(1) O_usercall; +v_init_val ->> (2) O_usercall; +v_elem_cnt ->>(3) O_usercall; +v_elem_size ->>(4) O_usercall; + +DELETE o_store; + +#0 ->> (0) o_cpy; +#1 ->> (1) o_ptr_add + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_pre_test.approved.txt b/src/compiler/tests/fixtures/rulecompile/exmp_pre_test.approved.txt new file mode 100644 index 0000000..cd7a240 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_pre_test.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/rulecompile/exmp_pre_test.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 0 +pattern_steps_count: 18 +action_steps_count: 6 + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_pre_test.rule b/src/compiler/tests/fixtures/rulecompile/exmp_pre_test.rule new file mode 100644 index 0000000..121b2dc --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_pre_test.rule @@ -0,0 +1,22 @@ + + v_map ->(0) o_addr; +#0x78 ->(0) o_word(USERDEFINED, _) -> v_word_res ->(1) o_addr -> vtmp ->(1) o_load(LOAD, _)-> v_mask_map; +v_bit ->(1) o_word; + + +#121 ->(0) o_shift(USERDEFINED, _) -> v_shift_res; +v_bit ->(1) o_shift; + +v_mask_map ->(0) o_right(INT_RIGHT, _) -> v_right_res; +v_shift_res ->(1) o_right; + +v_right_res ->(0) o_and(INT_AND, _) -> v_and_res ->(0) o_some; +#1 ->(1) o_and + + +-- +DELETE o_and; + +#33 ->>(0) o_pretest(USERDEFINED BEFORE o_word) ->> v_and_res; +v_map ->>(1) o_pretest; +v_bit ->>(2) o_pretest \ No newline at end of file diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_set.approved.txt b/src/compiler/tests/fixtures/rulecompile/exmp_set.approved.txt new file mode 100644 index 0000000..eb6b2ee --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_set.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/rulecompile/exmp_set.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 0 +pattern_steps_count: 15 +action_steps_count: 5 + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_set.rule b/src/compiler/tests/fixtures/rulecompile/exmp_set.rule new file mode 100644 index 0000000..e482a26 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_set.rule @@ -0,0 +1,17 @@ +#117 ->(0) o_mask(USERDEFINED,_) -> v_mask_res; +v_bit ->(1) o_mask; + + v_map ->(0) o_addr; +#0x78 ->(0) o_word(USERDEFINED, _) -> v_word_res ->(1) o_addr -> vtmp ->(1) o_load(LOAD, _)-> v_mask_map; +v_bit ->(1) o_word; + +v_mask_res ->(0) o_or(INT_OR, _); +v_mask_map ->(1) o_or -> v_or_res ->(2) o_finstore(STORE, _) +-- +#26 ->>(0) o_set(USERDEFINED BEFORE o_mask); +v_map ->>(1) o_set; +v_bit ->>(2) o_set; + +DELETE o_finstore + + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_shift.approved.txt b/src/compiler/tests/fixtures/rulecompile/exmp_shift.approved.txt new file mode 100644 index 0000000..6f3dff0 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_shift.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/rulecompile/exmp_shift.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 0 +pattern_steps_count: 7 +action_steps_count: 5 + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_shift.rule b/src/compiler/tests/fixtures/rulecompile/exmp_shift.rule new file mode 100644 index 0000000..1dbbf1a --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_shift.rule @@ -0,0 +1,7 @@ +#0 -> (1) o_to_byte(SUBPIECE, _); +v_bit ->(0) o_to_byte -> v_bit_1-> (0) o_and(INT_AND, _) -> v_res; +#0x3f -> (1)o_and +-- +#121 ->>(0) o_shift(USERDEFINED BEFORE o_and) ->> v_res; +v_bit ->>(1) o_shift; +DELETE o_and \ No newline at end of file diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_test.approved.txt b/src/compiler/tests/fixtures/rulecompile/exmp_test.approved.txt new file mode 100644 index 0000000..f36c20e --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_test.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/rulecompile/exmp_test.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 0 +pattern_steps_count: 8 +action_steps_count: 6 + diff --git a/src/compiler/tests/fixtures/rulecompile/exmp_test.rule b/src/compiler/tests/fixtures/rulecompile/exmp_test.rule new file mode 100644 index 0000000..8a59340 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/exmp_test.rule @@ -0,0 +1,17 @@ +#33 ->(0) o_pretest(USERDEFINED, _) -> v_pre_test_res; +v_map ->(1) o_pretest; +v_bit ->(2) o_pretest; + +v_pre_test_res ->(0) o_cond(INT_NOTEQUAL, _) -> v_cond_res; +#0 ->(1) o_cond + + +-- +#36 ->>(0) o_test(USERDEFINED BEFORE o_pretest) ->> v_cond_res; +v_map ->>(1) o_test; +v_bit ->>(2) o_test; + + + +DELETE o_cond + diff --git a/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec.approved.txt b/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec.approved.txt new file mode 100644 index 0000000..0234519 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/rulecompile/new_varnode_autospec.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 0 +pattern_steps_count: 2 +action_steps_count: 2 + diff --git a/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec.rule b/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec.rule new file mode 100644 index 0000000..588b5c0 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec.rule @@ -0,0 +1,3 @@ +v1 ->(0) op(INT_ADD, _) +-- +v_new ->>(0) op \ No newline at end of file diff --git a/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec_inv.approved.txt b/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec_inv.approved.txt new file mode 100644 index 0000000..4eefc57 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec_inv.approved.txt @@ -0,0 +1,8 @@ +fixture: fixtures/rulecompile/new_varnode_autospec_inv.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 1 +compile_error: Can't get size value for new varnode v_new + diff --git a/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec_inv.rule b/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec_inv.rule new file mode 100644 index 0000000..d77a940 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/new_varnode_autospec_inv.rule @@ -0,0 +1,3 @@ +v1 ->(0) op(CALL, _) +-- +v_new ->>(0) op \ No newline at end of file diff --git a/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type.approved.txt b/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type.approved.txt new file mode 100644 index 0000000..1d0e090 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type.approved.txt @@ -0,0 +1,8 @@ +fixture: fixtures/rulecompile/no_pnode_with_type.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 1 +compile_error: Pattern compilation requires at least one pnode with op type + diff --git a/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type.rule b/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type.rule new file mode 100644 index 0000000..1f498fb --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type.rule @@ -0,0 +1,5 @@ +v1 -> (0) op1 -> vout; +v2 -> (1) op1 +-- +v1 ->> (1) o_userdef(USERDEFINED AFTER op1); +#1 ->> (0) o_userdef \ No newline at end of file diff --git a/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type_component.approved.txt b/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type_component.approved.txt new file mode 100644 index 0000000..aefad72 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type_component.approved.txt @@ -0,0 +1,8 @@ +fixture: fixtures/rulecompile/no_pnode_with_type_component.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 1 +compile_error: Pattern graph contains nodes that cannot be reached from any typed pnode: op2 v3 vout2 + diff --git a/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type_component.rule b/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type_component.rule new file mode 100644 index 0000000..b5d77df --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/no_pnode_with_type_component.rule @@ -0,0 +1,10 @@ +v1 -> (0) op1(INT_ADD, _) -> vout; +v2 -> (1) op1; + +v3 -> (0) op2 -> vout2; +v3 -> (1) op2 + +-- +#1 ->> (0) o_userdef; +v1 ->> (1) o_userdef(USERDEFINED AFTER op1); +v3 ->> (2) o_userdef \ No newline at end of file diff --git a/src/compiler/tests/fixtures/rulecompile/sanity.approved.txt b/src/compiler/tests/fixtures/rulecompile/sanity.approved.txt new file mode 100644 index 0000000..b8bb74c --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/sanity.approved.txt @@ -0,0 +1,9 @@ +fixture: fixtures/rulecompile/sanity.rule +parse_status: 0 +stderr: + +validate_status: 0 +compile_status: 0 +pattern_steps_count: 1 +action_steps_count: 0 + diff --git a/src/compiler/tests/fixtures/rulecompile/sanity.rule b/src/compiler/tests/fixtures/rulecompile/sanity.rule new file mode 100644 index 0000000..3884b2d --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/sanity.rule @@ -0,0 +1,3 @@ +op(INT_ADD, _) +-- +VEMPTY diff --git a/src/compiler/tests/fixtures/rulecompile/syntax_error.approved.txt b/src/compiler/tests/fixtures/rulecompile/syntax_error.approved.txt new file mode 100644 index 0000000..6cb6377 --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/syntax_error.approved.txt @@ -0,0 +1,5 @@ +fixture: fixtures/rulecompile/syntax_error.rule +parse_status: 1 +stderr: +Parser error: fixtures/rulecompile/syntax_error.rule:2:1: syntax error, unexpected varnode name, expecting -- or ; + diff --git a/src/compiler/tests/fixtures/rulecompile/syntax_error.rule b/src/compiler/tests/fixtures/rulecompile/syntax_error.rule new file mode 100644 index 0000000..9dc4bcd --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/syntax_error.rule @@ -0,0 +1,2 @@ +v_orig -> (0) o_copy(COPY, _) +v_orig ->> (0) o_copy diff --git a/src/compiler/tests/fixtures/rulecompile/validate_error.approved.txt b/src/compiler/tests/fixtures/rulecompile/validate_error.approved.txt new file mode 100644 index 0000000..6d793cc --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/validate_error.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/rulecompile/validate_error.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Action Graph: Action cannot change whether varnode vtmp is defined by EMPTY pnode + diff --git a/src/compiler/tests/fixtures/rulecompile/validate_error.rule b/src/compiler/tests/fixtures/rulecompile/validate_error.rule new file mode 100644 index 0000000..af0b65a --- /dev/null +++ b/src/compiler/tests/fixtures/rulecompile/validate_error.rule @@ -0,0 +1,6 @@ +v1 -> (0) op1(INT_ADD, _) -> vtmp; + +op3(INT_AND, _) -> v2 -> (0) op2 +-- +op1 ->> v2; +op3 ->> v_new \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/act_inv_implicit_op_change.approved.txt b/src/compiler/tests/fixtures/validate/act_inv_implicit_op_change.approved.txt new file mode 100644 index 0000000..5d5c4ea --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_implicit_op_change.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/act_inv_implicit_op_change.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Action Graph: Action cannot change connections for pnode op3 because its operation type is undefined + diff --git a/src/compiler/tests/fixtures/validate/act_inv_implicit_op_change.rule b/src/compiler/tests/fixtures/validate/act_inv_implicit_op_change.rule new file mode 100644 index 0000000..5c670a3 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_implicit_op_change.rule @@ -0,0 +1,6 @@ +v1 -> (0) op1(INT_ADD, _) -> vtmp; + +op3 -> v2 -> (0) op2 +-- +op1 ->> v2; +#1 ->> (0) op_new(COPY AFTER op1) ->> vtmp diff --git a/src/compiler/tests/fixtures/validate/act_inv_op_without_type.approved.txt b/src/compiler/tests/fixtures/validate/act_inv_op_without_type.approved.txt new file mode 100644 index 0000000..8a5d6d4 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_op_without_type.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/act_inv_op_without_type.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Action Graph: Action cannot change connections for pnode op1 because its operation type is undefined + diff --git a/src/compiler/tests/fixtures/validate/act_inv_op_without_type.rule b/src/compiler/tests/fixtures/validate/act_inv_op_without_type.rule new file mode 100644 index 0000000..353904b --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_op_without_type.rule @@ -0,0 +1,4 @@ +v1 -> (0) op1 + +-- +#2 ->> (0) op1 diff --git a/src/compiler/tests/fixtures/validate/act_inv_trans_inf.approved.txt b/src/compiler/tests/fixtures/validate/act_inv_trans_inf.approved.txt new file mode 100644 index 0000000..f951b2c --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_trans_inf.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/act_inv_trans_inf.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Inference Action Graph: Can't unite 8 and 1 + diff --git a/src/compiler/tests/fixtures/validate/act_inv_trans_inf.rule b/src/compiler/tests/fixtures/validate/act_inv_trans_inf.rule new file mode 100644 index 0000000..f0c6c6d --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_trans_inf.rule @@ -0,0 +1,4 @@ +v1(1, _) -> (0) op1(INT_ADD, _) +-- + +#1 ->>(0) op_new(COPY BEFORE op1) ->> v2(8) ->> (1) op1 \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/act_inv_varnode_explicit_def.approved.txt b/src/compiler/tests/fixtures/validate/act_inv_varnode_explicit_def.approved.txt new file mode 100644 index 0000000..1b082b1 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_varnode_explicit_def.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/act_inv_varnode_explicit_def.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Build Action Graph (Action Step): Varnode v2 should has explicit define pnode + diff --git a/src/compiler/tests/fixtures/validate/act_inv_varnode_explicit_def.rule b/src/compiler/tests/fixtures/validate/act_inv_varnode_explicit_def.rule new file mode 100644 index 0000000..d9ca2ce --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_varnode_explicit_def.rule @@ -0,0 +1,5 @@ +v1 -> (0) op1(INT_ADD, _); + +v2 -> (0) op2 +-- +op1 ->> v2 diff --git a/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def.approved.txt b/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def.approved.txt new file mode 100644 index 0000000..9c8059e --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/act_inv_varnode_no_def.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Action Graph: Action cannot change whether varnode vtmp is defined by EMPTY pnode + diff --git a/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def.rule b/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def.rule new file mode 100644 index 0000000..af0b65a --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def.rule @@ -0,0 +1,6 @@ +v1 -> (0) op1(INT_ADD, _) -> vtmp; + +op3(INT_AND, _) -> v2 -> (0) op2 +-- +op1 ->> v2; +op3 ->> v_new \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def_ghostnode.approved.txt b/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def_ghostnode.approved.txt new file mode 100644 index 0000000..223124e --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def_ghostnode.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/act_inv_varnode_no_def_ghostnode.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Action Graph: Action cannot change whether varnode _ghost_out_of_op1 is defined by EMPTY pnode + diff --git a/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def_ghostnode.rule b/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def_ghostnode.rule new file mode 100644 index 0000000..04b05e2 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_inv_varnode_no_def_ghostnode.rule @@ -0,0 +1,6 @@ +v1 -> (0) op1(INT_ADD, _); + +op3(INT_AND, _) -> v2 -> (0) op2 +-- +op1 ->> v2; +op3 ->> v_new \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/act_op_any_type.approved.txt b/src/compiler/tests/fixtures/validate/act_op_any_type.approved.txt new file mode 100644 index 0000000..eda1111 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_op_any_type.approved.txt @@ -0,0 +1,10 @@ +fixture: fixtures/validate/act_op_any_type.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + +required_conditions: + + diff --git a/src/compiler/tests/fixtures/validate/act_op_any_type.rule b/src/compiler/tests/fixtures/validate/act_op_any_type.rule new file mode 100644 index 0000000..817de36 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/act_op_any_type.rule @@ -0,0 +1,4 @@ +v1 -> (0) op1(_, _) + +-- +#2 ->> (0) op1 diff --git a/src/compiler/tests/fixtures/validate/big_rule.approved.txt b/src/compiler/tests/fixtures/validate/big_rule.approved.txt new file mode 100644 index 0000000..00d35f6 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/big_rule.approved.txt @@ -0,0 +1,12 @@ +fixture: fixtures/validate/big_rule.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + 1==size_of_vn_v_flag_0 + bb_of_op_o_extend_flag->(0)bb_of_op_o_loop + bb_of_op_o_loop->(1)bb_of_op_o_extend_flag +required_conditions: + + diff --git a/src/compiler/tests/fixtures/validate/big_rule.rule b/src/compiler/tests/fixtures/validate/big_rule.rule new file mode 100644 index 0000000..f792cdb --- /dev/null +++ b/src/compiler/tests/fixtures/validate/big_rule.rule @@ -0,0 +1,35 @@ +v_flag_0(1, _) ->(0) o_extend_flag(INT_ZEXT, bb_body) -> v_always_0; + +v_always_0 ->(0) o_mult_zero(INT_MULT, _); +#-2 ->(1) o_mult_zero -> vtmp_alw0 ->(0) o_tmp_add(INT_ADD, _); + #1 ->(1) o_tmp_add -> vtmp1 ->(1) o_ptr_add(_,_); + +v_elem_size ->(2) o_ptr_add; +v_cur1 ->(0) o_ptr_add -> v_curtmp ->(0) o_copytmp(COPY, _) -> v_cur2; + +v_orig_prt ->(0) o_cpy1(COPY, _) -> v_cur_tmp ->(0) o_get_cur -> v_cur1; + v_cur2 ->(1) o_get_cur; + +v_cur1 ->(1) o_store(STORE, _); +v_init_val ->(2) o_store; + +#0 -> (1) o_cond(INT_NOTEQUAL, _) -> vtmp2 -> (1) o_loop(CBRANCH, bb_precond); +v_elem_cnt ->(0) o_cpy(COPY, _) -> v_cnt_orig ->(0) o_cnt -> v_cnt ->(0) o_cond; + +bb_body ->(0) bb_precond; +bb_precond ->(1) bb_body + +-- +O_usercall(USERDEFINED BEFORE o_cpy); + +#76 ->>(0) O_usercall; +v_orig_prt ->>(1) O_usercall; +v_init_val ->> (2) O_usercall; +v_elem_cnt ->>(3) O_usercall; +v_elem_size ->>(4) O_usercall; + +DELETE o_store; + +#0 ->> (0) o_cpy; +#1 ->> (1) o_ptr_add + diff --git a/src/compiler/tests/fixtures/validate/conds_dom.approved.txt b/src/compiler/tests/fixtures/validate/conds_dom.approved.txt new file mode 100644 index 0000000..967663c --- /dev/null +++ b/src/compiler/tests/fixtures/validate/conds_dom.approved.txt @@ -0,0 +1,12 @@ +fixture: fixtures/validate/conds_dom.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + +required_conditions: + bb_of_op_op1<=bb_of_op_op0 + bb_of_op_opA<=bb_of_op_op2 + size_of_vn_vA==size_of_vn_v0 + diff --git a/src/compiler/tests/fixtures/validate/conds_dom.rule b/src/compiler/tests/fixtures/validate/conds_dom.rule new file mode 100644 index 0000000..85b0fd3 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/conds_dom.rule @@ -0,0 +1,5 @@ +op0(INT_ADD, b0) -> v0 ->(0) op1(INT_ADD, b1) -> v1 ->(0) op2(INT_ADD, b2); +opA(INT_ADD, bA) -> vA +-- +vA ->> (0) op2; +v1 ->> (0) op0 \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/conds_trans_dom.approved.txt b/src/compiler/tests/fixtures/validate/conds_trans_dom.approved.txt new file mode 100644 index 0000000..e4fc0c0 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/conds_trans_dom.approved.txt @@ -0,0 +1,10 @@ +fixture: fixtures/validate/conds_trans_dom.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + +required_conditions: + + diff --git a/src/compiler/tests/fixtures/validate/conds_trans_dom.rule b/src/compiler/tests/fixtures/validate/conds_trans_dom.rule new file mode 100644 index 0000000..a5be793 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/conds_trans_dom.rule @@ -0,0 +1,3 @@ +op0(INT_ADD, b0) -> v0 ->(0) op1(INT_ADD, b1) -> v1 ->(0) op2(INT_ADD, b2) +-- +v0 ->> (0) op2 \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/ghost_nodes_rewritte_type.approved.txt b/src/compiler/tests/fixtures/validate/ghost_nodes_rewritte_type.approved.txt new file mode 100644 index 0000000..611e730 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/ghost_nodes_rewritte_type.approved.txt @@ -0,0 +1,10 @@ +fixture: fixtures/validate/ghost_nodes_rewritte_type.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + 8==size_of_vn_v1 +required_conditions: + + diff --git a/src/compiler/tests/fixtures/validate/ghost_nodes_rewritte_type.rule b/src/compiler/tests/fixtures/validate/ghost_nodes_rewritte_type.rule new file mode 100644 index 0000000..b9848c0 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/ghost_nodes_rewritte_type.rule @@ -0,0 +1,5 @@ +v1 (8, _) ->(0) op(INT_ADD, _) -> vout +-- +v2 (7) ->>(0) op ->> v4; +v3 ->>(0) op; +#1 ->>(0) op_new(COPY AFTER op) ->> vout diff --git a/src/compiler/tests/fixtures/validate/ghost_nodes_save_type.approved.txt b/src/compiler/tests/fixtures/validate/ghost_nodes_save_type.approved.txt new file mode 100644 index 0000000..ce7834b --- /dev/null +++ b/src/compiler/tests/fixtures/validate/ghost_nodes_save_type.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/ghost_nodes_save_type.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Inference Action Graph: Can't unite 7 and 8 + diff --git a/src/compiler/tests/fixtures/validate/ghost_nodes_save_type.rule b/src/compiler/tests/fixtures/validate/ghost_nodes_save_type.rule new file mode 100644 index 0000000..26a8f54 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/ghost_nodes_save_type.rule @@ -0,0 +1,3 @@ +v1 (8, _) ->(0) op(INT_ADD, _) +-- +v2 (7) ->>(0) op \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/new_pnode.approved.txt b/src/compiler/tests/fixtures/validate/new_pnode.approved.txt new file mode 100644 index 0000000..75d334a --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_pnode.approved.txt @@ -0,0 +1,10 @@ +fixture: fixtures/validate/new_pnode.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + +required_conditions: + + diff --git a/src/compiler/tests/fixtures/validate/new_pnode.rule b/src/compiler/tests/fixtures/validate/new_pnode.rule new file mode 100644 index 0000000..9e7f6d4 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_pnode.rule @@ -0,0 +1,4 @@ +v1 ->(0) op(INT_ADD, _) +-- +v1 ->>(0) op_new(INT_ADD AFTER op); +v1 ->>(1) op_new ->> v_new diff --git a/src/compiler/tests/fixtures/validate/new_pnode_bad_struct.approved.txt b/src/compiler/tests/fixtures/validate/new_pnode_bad_struct.approved.txt new file mode 100644 index 0000000..373ae38 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_pnode_bad_struct.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/new_pnode_bad_struct.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Action Graph: Unexpected EMPTY as 1 arg of pnode op_new expected non-Empty + diff --git a/src/compiler/tests/fixtures/validate/new_pnode_bad_struct.rule b/src/compiler/tests/fixtures/validate/new_pnode_bad_struct.rule new file mode 100644 index 0000000..4cbad8b --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_pnode_bad_struct.rule @@ -0,0 +1,3 @@ +v1 ->(0) op(INT_ADD, _) +-- +v1 ->>(0) op_new(INT_ADD AFTER op) \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/new_pnode_no_spec.approved.txt b/src/compiler/tests/fixtures/validate/new_pnode_no_spec.approved.txt new file mode 100644 index 0000000..32ec8af --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_pnode_no_spec.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/new_pnode_no_spec.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Action Graph: New pnode op_new isn't specialized (OpType not set) + diff --git a/src/compiler/tests/fixtures/validate/new_pnode_no_spec.rule b/src/compiler/tests/fixtures/validate/new_pnode_no_spec.rule new file mode 100644 index 0000000..1d2c4ff --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_pnode_no_spec.rule @@ -0,0 +1,3 @@ +v1 ->(0) op(INT_ADD, _) +-- +v1 ->>(0) op_new \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/new_varnode.approved.txt b/src/compiler/tests/fixtures/validate/new_varnode.approved.txt new file mode 100644 index 0000000..0a026dd --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_varnode.approved.txt @@ -0,0 +1,10 @@ +fixture: fixtures/validate/new_varnode.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + +required_conditions: + 8==size_of_vn_v1 + diff --git a/src/compiler/tests/fixtures/validate/new_varnode.rule b/src/compiler/tests/fixtures/validate/new_varnode.rule new file mode 100644 index 0000000..09ee84e --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_varnode.rule @@ -0,0 +1,3 @@ +v1 ->(0) op(INT_ADD, _) +-- +v_new(8) ->>(0) op \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/new_varnode_witohout_spec.approved.txt b/src/compiler/tests/fixtures/validate/new_varnode_witohout_spec.approved.txt new file mode 100644 index 0000000..f0a6b63 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_varnode_witohout_spec.approved.txt @@ -0,0 +1,10 @@ +fixture: fixtures/validate/new_varnode_witohout_spec.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + +required_conditions: + 8==size_of_vn_v1 + diff --git a/src/compiler/tests/fixtures/validate/new_varnode_witohout_spec.rule b/src/compiler/tests/fixtures/validate/new_varnode_witohout_spec.rule new file mode 100644 index 0000000..09ee84e --- /dev/null +++ b/src/compiler/tests/fixtures/validate/new_varnode_witohout_spec.rule @@ -0,0 +1,3 @@ +v1 ->(0) op(INT_ADD, _) +-- +v_new(8) ->>(0) op \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/op_delete_invalid.approved.txt b/src/compiler/tests/fixtures/validate/op_delete_invalid.approved.txt new file mode 100644 index 0000000..19db6d6 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/op_delete_invalid.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/op_delete_invalid.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Action Graph: Action cannot change whether varnode vout is defined by EMPTY pnode + diff --git a/src/compiler/tests/fixtures/validate/op_delete_invalid.rule b/src/compiler/tests/fixtures/validate/op_delete_invalid.rule new file mode 100644 index 0000000..0429057 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/op_delete_invalid.rule @@ -0,0 +1,3 @@ +v1 ->(0) op(INT_ADD, _) -> vout +-- +DELETE op \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/op_delete_valid.approved.txt b/src/compiler/tests/fixtures/validate/op_delete_valid.approved.txt new file mode 100644 index 0000000..bbfc182 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/op_delete_valid.approved.txt @@ -0,0 +1,10 @@ +fixture: fixtures/validate/op_delete_valid.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + +required_conditions: + + diff --git a/src/compiler/tests/fixtures/validate/op_delete_valid.rule b/src/compiler/tests/fixtures/validate/op_delete_valid.rule new file mode 100644 index 0000000..aa356d5 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/op_delete_valid.rule @@ -0,0 +1,4 @@ +v1 ->(0) op(INT_ADD, _) -> vout +-- +DELETE op; +#1 ->>(0) op_new(COPY AFTER op) ->> vout \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/pat_inv_arg_of_opempty.approved.txt b/src/compiler/tests/fixtures/validate/pat_inv_arg_of_opempty.approved.txt new file mode 100644 index 0000000..b46390c --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_arg_of_opempty.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/pat_inv_arg_of_opempty.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Build Pattern Graph: EMPTY pnode cannot take v1 as an argument + diff --git a/src/compiler/tests/fixtures/validate/pat_inv_arg_of_opempty.rule b/src/compiler/tests/fixtures/validate/pat_inv_arg_of_opempty.rule new file mode 100644 index 0000000..3d65cbc --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_arg_of_opempty.rule @@ -0,0 +1,3 @@ +v1 -> (0) OPEMPTY +-- +VEMPTY \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/pat_inv_const_def.approved.txt b/src/compiler/tests/fixtures/validate/pat_inv_const_def.approved.txt new file mode 100644 index 0000000..74caec4 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_const_def.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/pat_inv_const_def.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Pattern Graph: Const varnode 1 defined by pnode op + diff --git a/src/compiler/tests/fixtures/validate/pat_inv_const_def.rule b/src/compiler/tests/fixtures/validate/pat_inv_const_def.rule new file mode 100644 index 0000000..176aaca --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_const_def.rule @@ -0,0 +1,3 @@ +op -> #1 +-- +VEMPTY \ No newline at end of file diff --git a/src/compiler/tests/fixtures/validate/pat_inv_double_def_vnode.approved.txt b/src/compiler/tests/fixtures/validate/pat_inv_double_def_vnode.approved.txt new file mode 100644 index 0000000..c4e6386 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_double_def_vnode.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/pat_inv_double_def_vnode.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Build Pattern Graph: Varnode v1 already had def pnode (op) during adding op2 as new one + diff --git a/src/compiler/tests/fixtures/validate/pat_inv_double_def_vnode.rule b/src/compiler/tests/fixtures/validate/pat_inv_double_def_vnode.rule new file mode 100644 index 0000000..63d6b0e --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_double_def_vnode.rule @@ -0,0 +1,4 @@ +op(INT_ADD, _) -> v1; +op2(INT_ADD, _) -> v1 +-- +VEMPTY diff --git a/src/compiler/tests/fixtures/validate/pat_inv_op_type.approved.txt b/src/compiler/tests/fixtures/validate/pat_inv_op_type.approved.txt new file mode 100644 index 0000000..e8dcfff --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_op_type.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/pat_inv_op_type.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Validate Pattern Graph: Unexpected v1 as 2 arg of pnode op expected Empty + diff --git a/src/compiler/tests/fixtures/validate/pat_inv_op_type.rule b/src/compiler/tests/fixtures/validate/pat_inv_op_type.rule new file mode 100644 index 0000000..66fbe32 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_op_type.rule @@ -0,0 +1,3 @@ +v1 -> (2) op(INT_ADD, _) +-- +VEMPTY diff --git a/src/compiler/tests/fixtures/validate/pat_inv_trans_inf.approved.txt b/src/compiler/tests/fixtures/validate/pat_inv_trans_inf.approved.txt new file mode 100644 index 0000000..5d64fb9 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_trans_inf.approved.txt @@ -0,0 +1,7 @@ +fixture: fixtures/validate/pat_inv_trans_inf.rule +parse_status: 0 +stderr: + +validate_status: 1 +validate_error: Inference User Conditions Pattern Graph: Can't unite 6 and 2 + diff --git a/src/compiler/tests/fixtures/validate/pat_inv_trans_inf.rule b/src/compiler/tests/fixtures/validate/pat_inv_trans_inf.rule new file mode 100644 index 0000000..590089a --- /dev/null +++ b/src/compiler/tests/fixtures/validate/pat_inv_trans_inf.rule @@ -0,0 +1,12 @@ +v1(a, _) -> (0) op1(INT_ADD, _); +v2(b, _) -> (1) op1; + +vtmp2(2, _) -> (0) op2(INT_MULT, _); +v2(b, _) -> (1) op2; + +v1(a, _) -> (0) op3(INT_AND, _); +vtm1(6, _) -> (1) op3 + + +-- +VEMPTY diff --git a/src/compiler/tests/fixtures/validate/sanity.approved.txt b/src/compiler/tests/fixtures/validate/sanity.approved.txt new file mode 100644 index 0000000..a14d297 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/sanity.approved.txt @@ -0,0 +1,10 @@ +fixture: fixtures/validate/sanity.rule +parse_status: 0 +stderr: + +validate_status: 0 +generated_user_conditions: + +required_conditions: + + diff --git a/src/compiler/tests/fixtures/validate/sanity.rule b/src/compiler/tests/fixtures/validate/sanity.rule new file mode 100644 index 0000000..4f7dc34 --- /dev/null +++ b/src/compiler/tests/fixtures/validate/sanity.rule @@ -0,0 +1,3 @@ +VEMPTY +-- +VEMPTY diff --git a/src/compiler/tests/fixtures/validate/syntax_error.approved.txt b/src/compiler/tests/fixtures/validate/syntax_error.approved.txt new file mode 100644 index 0000000..bb9874b --- /dev/null +++ b/src/compiler/tests/fixtures/validate/syntax_error.approved.txt @@ -0,0 +1,5 @@ +fixture: fixtures/validate/syntax_error.rule +parse_status: 1 +stderr: +Parser error: fixtures/validate/syntax_error.rule:2:1: syntax error, unexpected varnode name, expecting -- or ; + diff --git a/src/compiler/tests/fixtures/validate/syntax_error.rule b/src/compiler/tests/fixtures/validate/syntax_error.rule new file mode 100644 index 0000000..9dc4bcd --- /dev/null +++ b/src/compiler/tests/fixtures/validate/syntax_error.rule @@ -0,0 +1,2 @@ +v_orig -> (0) o_copy(COPY, _) +v_orig ->> (0) o_copy diff --git a/src/compiler/tests/golden_common.cpp b/src/compiler/tests/golden_common.cpp new file mode 100644 index 0000000..355288b --- /dev/null +++ b/src/compiler/tests/golden_common.cpp @@ -0,0 +1,73 @@ +#include "golden_common.hh" + +#include "ApprovalTests.hpp" +#include "ApprovalTests/core/ApprovalNamer.h" + +#include +#include +#include +#include +#include + +namespace golden { + +namespace fs = std::filesystem; + +namespace { + +class FixtureAdjacentNamer : public ApprovalTests::ApprovalNamer { +public: + explicit FixtureAdjacentNamer(fs::path fixturePath) + : outputBase(std::move(fixturePath)) { + outputBase.replace_extension(); + } + + std::string getApprovedFile(std::string extensionWithDot) const override { + return pathFor("approved", extensionWithDot); + } + + std::string getReceivedFile(std::string extensionWithDot) const override { + return pathFor("received", extensionWithDot); + } + +private: + std::string + pathFor(const std::string &kind, const std::string &extensionWithDot) const { + fs::path output = outputBase; + output += "." + kind + extensionWithDot; + return output.string(); + } + + fs::path outputBase; +}; + +} // namespace + +ScopedCerrRedirect::ScopedCerrRedirect(std::ostream &replacement) + : originalBuffer(std::cerr.rdbuf(replacement.rdbuf())) {} + +ScopedCerrRedirect::~ScopedCerrRedirect() { std::cerr.rdbuf(originalBuffer); } + +fs::path displayPath(const fs::path &fixturePath) { + return fs::relative(fixturePath, fs::current_path()); +} + +int verifyFixtureOutput( + const fs::path &fixturePath, const std::string &output +) { + try { + static const ApprovalTests::QuietReporter quietReporter; + ApprovalTests::Approvals::verify( + output, + ApprovalTests::Options() + .withReporter(quietReporter) + .withNamer(std::make_shared(fixturePath)) + ); + return 0; + } catch (const std::exception &ex) { + std::cerr << ex.what() << "\n"; + return 1; + } +} + +} // namespace golden diff --git a/src/compiler/tests/golden_common.hh b/src/compiler/tests/golden_common.hh new file mode 100644 index 0000000..013e684 --- /dev/null +++ b/src/compiler/tests/golden_common.hh @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace golden { + +class ScopedCerrRedirect { +public: + explicit ScopedCerrRedirect(std::ostream &replacement); + ~ScopedCerrRedirect(); + + ScopedCerrRedirect(const ScopedCerrRedirect &) = delete; + ScopedCerrRedirect &operator=(const ScopedCerrRedirect &) = delete; + +private: + std::streambuf *originalBuffer; +}; + +std::filesystem::path displayPath(const std::filesystem::path &fixturePath); +int verifyFixtureOutput( + const std::filesystem::path &fixturePath, const std::string &output +); + +} // namespace golden diff --git a/src/compiler/tests/meson.build b/src/compiler/tests/meson.build new file mode 100644 index 0000000..9d54a40 --- /dev/null +++ b/src/compiler/tests/meson.build @@ -0,0 +1,81 @@ +approvaltests_dep = dependency( + 'approvaltests', + fallback: ['approvaltests', 'approvaltests_dep'], +) + +golden_common_src = ['golden_common.cpp'] + +parser_golden_test = executable( + 'parser_golden_test', + golden_common_src + [ + 'parser_golden_test.cpp', + ], + include_directories: [ + inc, + ], + dependencies: [ + parser_lib_dep, + approvaltests_dep, + ], +) + +validate_golden_test = executable( + 'validate_golden_test', + golden_common_src + [ + 'validate_golden_test.cpp', + ], + include_directories: [ + inc, + ], + dependencies: [ + parser_lib_dep, + validator_lib_dep, + approvaltests_dep, + ], +) + +rulecompile_golden_test = executable( + 'rulecompile_golden_test', + golden_common_src + [ + 'rulecompile_golden_test.cpp', + ], + include_directories: [ + inc, + ], + dependencies: [ + parser_lib_dep, + validator_lib_dep, + rulecompile_lib_dep, + approvaltests_dep, + ], +) + +golden_tests = { + 'parser': parser_golden_test, + 'validate': validate_golden_test, + 'rulecompile': rulecompile_golden_test, +} + +foreach stage, runner : golden_tests + fixtures_find = run_command( + 'find', + 'fixtures' / stage, + '-type', + 'f', + '-name', + '*.rule', + check: true, + ) + + fixtures = fixtures_find.stdout().strip().split('\n') + + foreach fixture : fixtures + fixture_name = stage + ' / ' + fixture.split('/')[-1] + test( + fixture_name, + runner, + args: [fixture], + workdir: meson.current_source_dir(), + ) + endforeach +endforeach diff --git a/src/compiler/tests/parser_golden_test.cpp b/src/compiler/tests/parser_golden_test.cpp new file mode 100644 index 0000000..f36a40d --- /dev/null +++ b/src/compiler/tests/parser_golden_test.cpp @@ -0,0 +1,51 @@ +#include "golden_common.hh" +#include "parse/ast_print.hh" +#include "parse/driver.hh" + +#include +#include +#include +#include + +namespace { + +namespace fs = std::filesystem; + +std::string renderFixture(const fs::path &fixturePath) { + std::ostringstream errors; + yy::Driver driver; + + int parseStatus = 0; + { + golden::ScopedCerrRedirect redirect(errors); + parseStatus = driver.parse(fixturePath); + } + + std::ostringstream output; + output << "fixture: " << golden::displayPath(fixturePath).string() << "\n"; + output << "parse_status: " << parseStatus << "\n"; + output << "stderr:\n"; + + const std::string errorText = errors.str(); + output << (errorText.empty() ? "\n" : errorText); + + if (parseStatus == 0) { + output << "ast:\n"; + ast::print::rule(output, driver.getParsedRule()); + output << "\n"; + } + + return output.str(); +} + +} // namespace + +int main(int argc, char **argv) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] << " \n"; + return 2; + } + + const fs::path fixturePath = argv[1]; + return golden::verifyFixtureOutput(fixturePath, renderFixture(fixturePath)); +} diff --git a/src/compiler/tests/rulecompile_golden_test.cpp b/src/compiler/tests/rulecompile_golden_test.cpp new file mode 100644 index 0000000..87d2b9c --- /dev/null +++ b/src/compiler/tests/rulecompile_golden_test.cpp @@ -0,0 +1,90 @@ +#include "compiled_rule.hh" +#include "golden_common.hh" +#include "parse/driver.hh" +#include "rulecompile/rulecompile_driver.hh" +#include "validate/validate_driver.hh" + +#include +#include +#include +#include +#include + +namespace { + +namespace fs = std::filesystem; +namespace compiled = pcodeweaver::compiled; + +void printCompiledRule(std::ostream &out, const compiled::Rule &rule) { + out << "pattern_steps_count: " << rule.pattern.steps.size() << "\n"; + out << "action_steps_count: " << rule.action.steps.size() << "\n"; +} + +std::string renderFixture(const fs::path &fixturePath) { + std::ostringstream errors; + yy::Driver parseDriver; + + int parseStatus = 0; + { + golden::ScopedCerrRedirect redirect(errors); + parseStatus = parseDriver.parse(fixturePath); + } + + std::ostringstream output; + output << "fixture: " << golden::displayPath(fixturePath).string() << "\n"; + output << "parse_status: " << parseStatus << "\n"; + output << "stderr:\n"; + + const std::string errorText = errors.str(); + output << (errorText.empty() ? "\n" : errorText); + + if (parseStatus != 0) { + return output.str(); + } + + const ast::Rule &rule = parseDriver.getParsedRule(); + ValidateDriver validateDriver(parseDriver.getContext()); + auto validateRes = validateDriver.validate(rule); + + output << "validate_status: " << (validateRes.has_value() ? 0 : 1) << "\n"; + if (!validateRes.has_value()) { + output << "validate_error: " << validateRes.error().message() << "\n"; + return output.str(); + } + + std::vector runtimeChecks = + validateDriver.getRuntimeCheckConditions(); + RuleCompileDriver compileDriver( + RuleCompileInput{ + .actions = rule.actions, + .patternGraph = validateDriver.getPGraph(), + .actionGraph = validateDriver.getActGraph(), + .sizeSolver = validateDriver.getSizeSolver(), + .runtimeChecks = runtimeChecks, + .runtimeValueRequirements = + validateDriver.getRuntimeValueRequirements(), + } + ); + + auto compileRes = compileDriver.compile(); + output << "compile_status: " << (compileRes.has_value() ? 0 : 1) << "\n"; + if (!compileRes.has_value()) { + output << "compile_error: " << compileRes.error().message() << "\n"; + return output.str(); + } + + printCompiledRule(output, compileRes.value()); + return output.str(); +} + +} // namespace + +int main(int argc, char **argv) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] << " \n"; + return 2; + } + + const fs::path fixturePath = argv[1]; + return golden::verifyFixtureOutput(fixturePath, renderFixture(fixturePath)); +} diff --git a/src/compiler/tests/validate_golden_test.cpp b/src/compiler/tests/validate_golden_test.cpp new file mode 100644 index 0000000..bc57252 --- /dev/null +++ b/src/compiler/tests/validate_golden_test.cpp @@ -0,0 +1,80 @@ +#include "golden_common.hh" +#include "parse/driver.hh" +#include "validate/validate_driver.hh" + +#include +#include +#include +#include +#include + +namespace { + +namespace fs = std::filesystem; + +void printConditionList( + std::ostream &out, const std::string &label, + const std::vector &conditions +) { + out << label << ":\n"; + if (conditions.empty()) { + out << " \n"; + return; + } + + for (const speccond::SpecCondition &condition : conditions) { + out << " " << condition << "\n"; + } +} + +std::string renderFixture(const fs::path &fixturePath) { + std::ostringstream errors; + yy::Driver parseDriver; + + int parseStatus = 0; + { + golden::ScopedCerrRedirect redirect(errors); + parseStatus = parseDriver.parse(fixturePath); + } + + std::ostringstream output; + output << "fixture: " << golden::displayPath(fixturePath).string() << "\n"; + output << "parse_status: " << parseStatus << "\n"; + output << "stderr:\n"; + + const std::string errorText = errors.str(); + output << (errorText.empty() ? "\n" : errorText); + + if (parseStatus != 0) { + return output.str(); + } + + ValidateDriver validateDriver(parseDriver.getContext()); + const Errorable validateRes = + validateDriver.validate(parseDriver.getParsedRule()); + + output << "validate_status: " << (validateRes.has_value() ? 0 : 1) << "\n"; + if (!validateRes.has_value()) { + output << "validate_error: " << validateRes.error().message() << "\n"; + return output.str(); + } + + printConditionList( + output, "generated_user_conditions", validateDriver.getURTC() + ); + printConditionList(output, "required_conditions", validateDriver.getRQC()); + + return output.str(); +} + +} // namespace + +int main(int argc, char **argv) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] << " \n"; + return 2; + } + + const fs::path fixturePath = argv[1]; + return golden::verifyFixtureOutput(fixturePath, renderFixture(fixturePath)); +} diff --git a/src/compiler/validate/action_pcodegraph.cpp b/src/compiler/validate/action_pcodegraph.cpp index bcdcb87..8823833 100644 --- a/src/compiler/validate/action_pcodegraph.cpp +++ b/src/compiler/validate/action_pcodegraph.cpp @@ -12,13 +12,28 @@ ActionPcodeGraph::ActionPcodeGraph(const PcodeGraph &base) : PcodeGraph(base), patternVarnodeDefEmpty() { for (const auto &[_id, gv] : liveVarnodes()) { const VarGraphNode *node = get_if_uniq(gv); - if (node == nullptr || node->isGhost || !node->edges.def.has_value()) { + if (node == nullptr || !node->edges.def.has_value()) { continue; } patternVarnodeDefEmpty.emplace( node->id, node->edges.def.value() == nullptr ); } + + for (const auto &[id, gp] : livePnodes()) { + const OpGraphNode &node = *unpackGP(*gp); + if (node.isGhost || node.opTp.has_value()) { + continue; + } + + patternPnodeConnectionsWithoutType.emplace( + id, + PatternPnodeConnections{ + .inrefs = node.edges.inrefs, + .output = node.edges.output, + } + ); + } } Errorable NewOpGraphNode::addSpec(const ast::PnodeSpecTypeAndLoc &spec) { @@ -99,13 +114,6 @@ ActionPcodeGraph::validateExistingPnodeActionTarget(GraphPnode *gp) { return gp; } - if (!og.opTp.has_value()) { - return err( - "Action cannot operate on pnode " + og.id.getName() + - " because its operation type is undefined" - ); - } - return gp; } @@ -386,6 +394,45 @@ Errorable ActionPcodeGraph::validateExistingVarnodeDefStability( ); } +Errorable ActionPcodeGraph::validateExistingPnodeWithoutTypeStability( + const OpGraphNode &node +) { + auto original = patternPnodeConnectionsWithoutType.find(node.id); + if (original == patternPnodeConnectionsWithoutType.end()) { + return {}; + } + + if (node.edges.output != original->second.output || + node.edges.inrefs != original->second.inrefs) { + return err( + "Action cannot change connections for pnode " + node.id.getName() + + " because its operation type is undefined" + ); + } + + return {}; +} + +Errorable ActionPcodeGraph::validateExistingPnodesWithoutTypeStability() { + for (const auto &[id, _connections] : patternPnodeConnectionsWithoutType) { + auto it = pnodes.find(id); + if (it == pnodes.end() || isDeleted(*it->second)) { + return err( + "Action cannot delete pnode " + id.getName() + + " because its operation type is undefined" + ); + } + + auto res = + validateExistingPnodeWithoutTypeStability(*unpackGP(*it->second)); + if (!res.has_value()) { + return res; + } + } + + return {}; +} + Errorable ActionPcodeGraph::validateNewPnode(const NewOpGraphNode &node) { if (!node.opTp.has_value()) { return err( @@ -420,6 +467,11 @@ Errorable ActionPcodeGraph::validate() { } } + res = validateExistingPnodesWithoutTypeStability(); + if (!res.has_value()) { + return res; + } + for (const auto &[_id, newVarnode] : newVarnodes) { auto res = validateNewVarnode(*newVarnode); if (!res.has_value()) { diff --git a/src/compiler/validate/action_pcodegraph.hh b/src/compiler/validate/action_pcodegraph.hh index b133235..6f57d7c 100644 --- a/src/compiler/validate/action_pcodegraph.hh +++ b/src/compiler/validate/action_pcodegraph.hh @@ -44,6 +44,9 @@ protected: Errorable addEmptyAction(const ast::EmptyAction &ea); Errorable addAction(const ast::RuleAction &act); Errorable validateExistingVarnodeDefStability(const VarGraphNode &node); + Errorable + validateExistingPnodeWithoutTypeStability(const OpGraphNode &node); + Errorable validateExistingPnodesWithoutTypeStability(); Errorable validateNewVarnode(const NewVarGraphNode &node); Errorable validateNewPnode(const NewOpGraphNode &node); @@ -64,7 +67,14 @@ public: std::vector getNewPnodeIds() const; protected: + struct PatternPnodeConnections { + std::unordered_map inrefs; + GraphVarnode *output; + }; + std::unordered_map patternVarnodeDefEmpty; + std::unordered_map + patternPnodeConnectionsWithoutType; std::unordered_map newVarnodes; diff --git a/src/plugin/tests/README.md b/src/plugin/tests/README.md new file mode 100644 index 0000000..82525c4 --- /dev/null +++ b/src/plugin/tests/README.md @@ -0,0 +1,38 @@ +# Plugin System Tests + +These tests run the plugin through a fixed Ghidra/ReOxide Docker environment and +compare the raw decompiled `main` output with ApprovalTests files. + +Build the reusable base image once: + +```sh +docker build \ + -f src/plugin/tests/docker/base.Dockerfile \ + -t pcode-weaver-plugin-test-base:ghidra-12.0_reoxide-0.7.2 \ + . +``` + +Build the per-checkout test image: + +```sh +docker build --target plugin-test -t pcode-weaver-plugin-test . +``` + +Run one case directly: + +```sh +cd src/compiler +meson compile -j 1 -C build +cd ../.. +src/plugin/tests/scripts/run-docker-case.sh \ + src/plugin/tests/fixtures/01_obfuscated \ + pcode-weaver-plugin-test +``` + +Run through Meson/ApprovalTests: + +```sh +cd src/plugin/tests +meson setup build +meson test -C build --print-errorlogs +``` diff --git a/src/plugin/tests/docker/base.Dockerfile b/src/plugin/tests/docker/base.Dockerfile new file mode 100644 index 0000000..f57016e --- /dev/null +++ b/src/plugin/tests/docker/base.Dockerfile @@ -0,0 +1,59 @@ +FROM eclipse-temurin:21-jdk-jammy AS ghidra-download + +ARG GHIDRA_VERSION=12.0 +ARG GHIDRA_DATE=20251205 + +ENV DEBIAN_FRONTEND=noninteractive +ENV GHIDRA_INSTALL_DIR=/opt/ghidra + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ + set -ex;\ + apt-get update;\ + apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + unzip;\ + curl -L \ + "https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_${GHIDRA_VERSION}_build/ghidra_${GHIDRA_VERSION}_PUBLIC_${GHIDRA_DATE}.zip" \ + -o /tmp/ghidra.zip;\ + unzip -q /tmp/ghidra.zip -d /opt;\ + mv "/opt/ghidra_${GHIDRA_VERSION}_PUBLIC" "${GHIDRA_INSTALL_DIR}";\ + rm /tmp/ghidra.zip + +FROM eclipse-temurin:21-jdk-jammy + +ARG REOXIDE_VERSION=0.7.2 + +ENV DEBIAN_FRONTEND=noninteractive +ENV GHIDRA_INSTALL_DIR=/opt/ghidra +ENV VIRTUAL_ENV=/opt/reoxide/reoxide-venv +ENV PATH="${VIRTUAL_ENV}/bin:${PATH}" +ENV PIP_DISABLE_PIP_VERSION_CHECK=1 + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \ + set -ex;\ + apt-get update;\ + apt-get install -y --no-install-recommends \ + ca-certificates \ + python3 \ + python3-venv;\ + python3 -m venv "${VIRTUAL_ENV}" + +RUN --mount=type=cache,target=/root/.cache/pip \ + set -ex;\ + pip install "reoxide==${REOXIDE_VERSION}" + +COPY --from=ghidra-download /opt/ghidra /opt/ghidra + +RUN set -ex;\ + mkdir -p /opt/reoxide/data/plugins;\ + printf '%s\n' \ + 'data-directory = "/opt/reoxide/data"' \ + '' \ + '[[ghidra-install]]' \ + 'enabled = true' \ + "root-dir = \"${GHIDRA_INSTALL_DIR}\"" \ + > /opt/reoxide/reoxide.toml;\ + reoxide -c /opt/reoxide/reoxide.toml link-ghidra diff --git a/src/plugin/tests/fixtures/01_obfuscated/approved/main.approved.txt b/src/plugin/tests/fixtures/01_obfuscated/approved/main.approved.txt new file mode 100644 index 0000000..f9bb8d2 --- /dev/null +++ b/src/plugin/tests/fixtures/01_obfuscated/approved/main.approved.txt @@ -0,0 +1,8 @@ + +int main(uint param_1) + +{ + return param_1 + 1; +} + + diff --git a/src/plugin/tests/fixtures/01_obfuscated/rules/500_demo_obfuscated b/src/plugin/tests/fixtures/01_obfuscated/rules/500_demo_obfuscated new file mode 100644 index 0000000..cde8309 --- /dev/null +++ b/src/plugin/tests/fixtures/01_obfuscated/rules/500_demo_obfuscated @@ -0,0 +1,16 @@ +o_mul (INT_MULT, _); +o_add (INT_ADD, _); +o_and (INT_AND, _); +o_xor (INT_XOR, _); + +v_param -> (0) o_and; +#1 -> (1) o_and -> v_and ->(0) o_mul; + #2 ->(1) o_mul -> v_mul ->(0) o_add; + +v_param -> (0) o_xor -> v_xor ->(1) o_add; +#1 -> (1) o_xor + +-- +v_param ->> (0) o_add; +#1 ->> (1) o_add + diff --git a/src/plugin/tests/fixtures/01_obfuscated/target/obfuscated.elf b/src/plugin/tests/fixtures/01_obfuscated/target/obfuscated.elf new file mode 100755 index 0000000..fff7fbd Binary files /dev/null and b/src/plugin/tests/fixtures/01_obfuscated/target/obfuscated.elf differ diff --git a/src/plugin/tests/fixtures/02_obfuscated2/approved/main.approved.txt b/src/plugin/tests/fixtures/02_obfuscated2/approved/main.approved.txt new file mode 100644 index 0000000..767924b --- /dev/null +++ b/src/plugin/tests/fixtures/02_obfuscated2/approved/main.approved.txt @@ -0,0 +1,160 @@ + +int main(undefined4 param_1) + +{ + int iVar1; + uint uVar2; + uint uVar3; + uint uVar4; + uint uVar5; + undefined4 local_24; + + local_24 = 0; + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(param_1); + uVar3 = id(1); + iVar1 = id(2); + uVar4 = id(param_1); + uVar5 = id(1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(param_1); + uVar3 = id(1); + iVar1 = id(2); + uVar4 = id(1); + uVar5 = id(param_1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(1); + uVar3 = id(param_1); + iVar1 = id(2); + uVar4 = id(param_1); + uVar5 = id(1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(1); + uVar3 = id(param_1); + iVar1 = id(2); + uVar4 = id(1); + uVar5 = id(param_1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + iVar1 = id(2); + uVar2 = id(param_1); + uVar3 = id(1); + uVar4 = id(param_1); + uVar5 = id(1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + iVar1 = id(2); + uVar2 = id(param_1); + uVar3 = id(1); + uVar4 = id(1); + uVar5 = id(param_1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + iVar1 = id(2); + uVar2 = id(1); + uVar3 = id(param_1); + uVar4 = id(param_1); + uVar5 = id(1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + iVar1 = id(2); + uVar2 = id(1); + uVar3 = id(param_1); + uVar4 = id(1); + uVar5 = id(param_1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(param_1); + uVar3 = id(1); + uVar4 = id(param_1); + uVar5 = id(1); + iVar1 = id(2); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(1); + uVar3 = id(param_1); + uVar4 = id(param_1); + uVar5 = id(1); + iVar1 = id(2); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(param_1); + uVar3 = id(1); + uVar4 = id(1); + uVar5 = id(param_1); + iVar1 = id(2); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(1); + uVar3 = id(param_1); + uVar4 = id(1); + uVar5 = id(param_1); + iVar1 = id(2); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(param_1); + uVar3 = id(1); + iVar1 = id(2); + uVar4 = id(param_1); + uVar5 = id(1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(1); + uVar3 = id(param_1); + iVar1 = id(2); + uVar4 = id(param_1); + uVar5 = id(1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(param_1); + uVar3 = id(1); + iVar1 = id(2); + uVar4 = id(1); + uVar5 = id(param_1); + local_24 = param_1 + 1; + } + iVar1 = id(1); + if (1 != 0) { + uVar2 = id(1); + uVar3 = id(param_1); + iVar1 = id(2); + uVar4 = id(1); + uVar5 = id(param_1); + local_24 = param_1 + 1; + } + return local_24; +} + + diff --git a/src/plugin/tests/fixtures/02_obfuscated2/rules/010_predemo b/src/plugin/tests/fixtures/02_obfuscated2/rules/010_predemo new file mode 100644 index 0000000..52e3d68 --- /dev/null +++ b/src/plugin/tests/fixtures/02_obfuscated2/rules/010_predemo @@ -0,0 +1,6 @@ +v_id_addr (_, _, 0x0101129) ->(0) o_call_id(CALL, _); +v_id_arg ->(1) o_call_id -> vres ->(0) o_op(_, _) + +-- + +v_id_arg ->> (0) o_op \ No newline at end of file diff --git a/src/plugin/tests/fixtures/02_obfuscated2/rules/011_predemo b/src/plugin/tests/fixtures/02_obfuscated2/rules/011_predemo new file mode 100644 index 0000000..f51a5d5 --- /dev/null +++ b/src/plugin/tests/fixtures/02_obfuscated2/rules/011_predemo @@ -0,0 +1,6 @@ +v_id_addr (_, _, 0x0101129) ->(0) o_call_id(CALL, _); +v_id_arg ->(1) o_call_id -> vres ->(1) o_op(_, _) + +-- + +v_id_arg ->> (1) o_op \ No newline at end of file diff --git a/src/plugin/tests/fixtures/02_obfuscated2/rules/100_demo_and_reorder b/src/plugin/tests/fixtures/02_obfuscated2/rules/100_demo_and_reorder new file mode 100644 index 0000000..e34ca60 --- /dev/null +++ b/src/plugin/tests/fixtures/02_obfuscated2/rules/100_demo_and_reorder @@ -0,0 +1,5 @@ +#1 -> (0) o_and(INT_AND, _); +v1 -> (1) o_and +-- +v1 ->> (0) o_and; +#1 ->> (1) o_and \ No newline at end of file diff --git a/src/plugin/tests/fixtures/02_obfuscated2/rules/110_demo_xor_reorder b/src/plugin/tests/fixtures/02_obfuscated2/rules/110_demo_xor_reorder new file mode 100644 index 0000000..37f083f --- /dev/null +++ b/src/plugin/tests/fixtures/02_obfuscated2/rules/110_demo_xor_reorder @@ -0,0 +1,5 @@ +#1 -> (0) o_xor(INT_XOR, _); +v1 -> (1) o_xor +-- +v1 ->> (0) o_xor; +#1 ->> (1) o_xor \ No newline at end of file diff --git a/src/plugin/tests/fixtures/02_obfuscated2/rules/120_demo_mul_reorder b/src/plugin/tests/fixtures/02_obfuscated2/rules/120_demo_mul_reorder new file mode 100644 index 0000000..dc9c994 --- /dev/null +++ b/src/plugin/tests/fixtures/02_obfuscated2/rules/120_demo_mul_reorder @@ -0,0 +1,5 @@ +#2 -> (0) o_mul(INT_MULT, _); +v1 -> (1) o_mul +-- +v1 ->> (0) o_mul; +#2 ->> (1) o_mul \ No newline at end of file diff --git a/src/plugin/tests/fixtures/02_obfuscated2/rules/200_demo_add_reorder b/src/plugin/tests/fixtures/02_obfuscated2/rules/200_demo_add_reorder new file mode 100644 index 0000000..8ee9cb9 --- /dev/null +++ b/src/plugin/tests/fixtures/02_obfuscated2/rules/200_demo_add_reorder @@ -0,0 +1,5 @@ +o_mul(INT_MULT, _) -> v_mul_res ->(1) o_add (INT_ADD, _); + v1 ->(0) o_add +-- +v1 ->> (1) o_add; +v_mul_res ->> (0) o_add \ No newline at end of file diff --git a/src/plugin/tests/fixtures/02_obfuscated2/rules/500_demo_obfuscated b/src/plugin/tests/fixtures/02_obfuscated2/rules/500_demo_obfuscated new file mode 100644 index 0000000..cde8309 --- /dev/null +++ b/src/plugin/tests/fixtures/02_obfuscated2/rules/500_demo_obfuscated @@ -0,0 +1,16 @@ +o_mul (INT_MULT, _); +o_add (INT_ADD, _); +o_and (INT_AND, _); +o_xor (INT_XOR, _); + +v_param -> (0) o_and; +#1 -> (1) o_and -> v_and ->(0) o_mul; + #2 ->(1) o_mul -> v_mul ->(0) o_add; + +v_param -> (0) o_xor -> v_xor ->(1) o_add; +#1 -> (1) o_xor + +-- +v_param ->> (0) o_add; +#1 ->> (1) o_add + diff --git a/src/plugin/tests/fixtures/02_obfuscated2/target/obfuscated2.elf b/src/plugin/tests/fixtures/02_obfuscated2/target/obfuscated2.elf new file mode 100755 index 0000000..ed3efa0 Binary files /dev/null and b/src/plugin/tests/fixtures/02_obfuscated2/target/obfuscated2.elf differ diff --git a/src/plugin/tests/fixtures/03_memset/approved/main.approved.txt b/src/plugin/tests/fixtures/03_memset/approved/main.approved.txt new file mode 100644 index 0000000..0fb3f51 --- /dev/null +++ b/src/plugin/tests/fixtures/03_memset/approved/main.approved.txt @@ -0,0 +1,153 @@ + +uint main(int param_1,long param_2) + +{ + undefined1 auVar1 [16]; + ulong uVar2; + long lVar3; + ulong uVar4; + ulong uVar5; + ulong *puVar6; + undefined8 *puVar7; + ulong *puVar8; + long *plVar9; + ulonglong uVar10; + uint uVar11; + long in_FS_OFFSET; + byte bVar12; + ulong local_148 [3]; + ulong local_130 [2]; + ulong local_120; + long local_118 [4]; + undefined8 local_f8 [4]; + ulong local_d8 [6]; + undefined8 local_a8 [4]; + ulong local_88; + ulong local_80; + ulong local_78; + ulong local_70; + ulong local_68 [9]; + long local_20; + + bVar12 = 0; + uVar10 = 0x2a; + local_20 = *(long *)(in_FS_OFFSET + 0x28); + if (1 < param_1) { + uVar10 = strtoull(*(char **)(param_2 + 8),(char **)0x0,0); + } + uVar11 = (uint)uVar10 & 3; + puVar6 = &local_88; + skinit(&local_88,0,0xc,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar6 = puVar6 + 1; + } + local_88 = uVar10; + local_70 = 0x1000; + auVar1._8_8_ = 0; + auVar1._0_8_ = uVar10; + local_80 = (uVar10 - ((SUB168(auVar1 * ZEXT816(0xf0f0f0f0f0f0f0f1),8) & 0xfffffffffffffff0) + + uVar10 / 0x11)) + 1000; + if ((uVar10 & 1) == 0) { + plVar9 = local_118; + skinit(local_118,-1,8,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + plVar9 = plVar9 + 1; + } + local_78 = local_78 | 1; + } + else { + plVar9 = local_118; + skinit(local_118,(ulong)((uint)uVar10 & 0xf) * 0x1111111111111111,4,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + plVar9 = plVar9 + 1; + } + puVar7 = local_f8; + skinit(local_f8,0xeeeeeeeeeeeeeeee,4,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar7 = puVar7 + 1; + } + local_78 = local_78 | 2; + } + uVar5 = 0; + uVar4 = 0; + puVar6 = local_d8; + do { + uVar2 = uVar10 >> ((byte)uVar4 & 0x3f); + if ((uVar2 & 1) == 0) { + puVar8 = puVar6; + skinit(puVar6,(ulong)((uint)uVar2 & 1),3,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar8 = puVar8 + 1; + } + } + else { + puVar8 = puVar6; + skinit(puVar6,uVar10 + uVar4 ^ 0xcccccccccccccccc,3,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar8 = puVar8 + 1; + } + } + uVar2 = uVar4 + 1; + uVar5 = uVar5 ^ local_118[uVar4] + puVar6[1]; + *puVar6 = uVar4 | 0x5041434b00000000; + uVar4 = uVar2; + puVar6 = puVar6 + 3; + } while (uVar2 != 3); + if (uVar11 == 1) { + puVar6 = local_148; + skinit(local_148,0xabababababababab,6,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar6 = puVar6 + 1; + } + local_120 = local_80; + } + else if (uVar11 == 2) { + puVar6 = local_148; + skinit(local_148,uVar5,3,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar6 = puVar6 + 1; + } + puVar6 = local_130; + skinit(local_130,~uVar5,3,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar6 = puVar6 + 1; + } + } + else if ((uVar10 & 3) == 0) { + puVar6 = local_148; + skinit(local_148,0,6,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar6 = puVar6 + 1; + } + local_148[0] = local_88; + } + else { + puVar6 = local_148; + skinit(local_148,uVar10 | 0xdead000000000000,6,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar6 = puVar6 + 1; + } + if (0x400 < local_70) { + puVar6 = local_68; + skinit(local_68,uVar10,8,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar6 = puVar6 + 1; + } + } + } + if ((local_148[0] ^ local_120) == uVar10) { + puVar7 = local_a8; + skinit(local_a8,0x7777777777777777,4,8); + for (lVar3 = 0; lVar3 != 0; lVar3 = lVar3 + -1) { + puVar7 = puVar7 + 1; + } + } + __printf_chk(1,"%llu %llu %llx %llx %llx\n",local_80,local_78,local_118[0],local_a8[0],local_120); + if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) { + /* WARNING: Subroutine does not return */ + __stack_chk_fail(); + } + return (uint)uVar5 & 7; +} + + diff --git a/src/plugin/tests/fixtures/03_memset/rules/300_memset b/src/plugin/tests/fixtures/03_memset/rules/300_memset new file mode 100644 index 0000000..f792cdb --- /dev/null +++ b/src/plugin/tests/fixtures/03_memset/rules/300_memset @@ -0,0 +1,35 @@ +v_flag_0(1, _) ->(0) o_extend_flag(INT_ZEXT, bb_body) -> v_always_0; + +v_always_0 ->(0) o_mult_zero(INT_MULT, _); +#-2 ->(1) o_mult_zero -> vtmp_alw0 ->(0) o_tmp_add(INT_ADD, _); + #1 ->(1) o_tmp_add -> vtmp1 ->(1) o_ptr_add(_,_); + +v_elem_size ->(2) o_ptr_add; +v_cur1 ->(0) o_ptr_add -> v_curtmp ->(0) o_copytmp(COPY, _) -> v_cur2; + +v_orig_prt ->(0) o_cpy1(COPY, _) -> v_cur_tmp ->(0) o_get_cur -> v_cur1; + v_cur2 ->(1) o_get_cur; + +v_cur1 ->(1) o_store(STORE, _); +v_init_val ->(2) o_store; + +#0 -> (1) o_cond(INT_NOTEQUAL, _) -> vtmp2 -> (1) o_loop(CBRANCH, bb_precond); +v_elem_cnt ->(0) o_cpy(COPY, _) -> v_cnt_orig ->(0) o_cnt -> v_cnt ->(0) o_cond; + +bb_body ->(0) bb_precond; +bb_precond ->(1) bb_body + +-- +O_usercall(USERDEFINED BEFORE o_cpy); + +#76 ->>(0) O_usercall; +v_orig_prt ->>(1) O_usercall; +v_init_val ->> (2) O_usercall; +v_elem_cnt ->>(3) O_usercall; +v_elem_size ->>(4) O_usercall; + +DELETE o_store; + +#0 ->> (0) o_cpy; +#1 ->> (1) o_ptr_add + diff --git a/src/plugin/tests/fixtures/03_memset/target/memset.elf b/src/plugin/tests/fixtures/03_memset/target/memset.elf new file mode 100755 index 0000000..0db178f Binary files /dev/null and b/src/plugin/tests/fixtures/03_memset/target/memset.elf differ diff --git a/src/plugin/tests/fixtures/04_bitmap/approved/main.approved.txt b/src/plugin/tests/fixtures/04_bitmap/approved/main.approved.txt new file mode 100644 index 0000000..4097ad1 --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/approved/main.approved.txt @@ -0,0 +1,89 @@ + +uint main(int param_1,long param_2) + +{ + byte bVar1; + ulong uVar2; + ulong uVar3; + long lVar4; + ulong uVar5; + ulong uVar6; + ulong *puVar7; + long in_FS_OFFSET; + int local_184; + ulong local_180; + ulong local_158 [19]; + undefined8 local_c0; + ulong local_b8 [21]; + long local_10; + + local_10 = *(long *)(in_FS_OFFSET + 0x28); + puVar7 = local_158; + for (lVar4 = 0x14; lVar4 != 0; lVar4 = lVar4 + -1) { + *puVar7 = 0; + puVar7 = puVar7 + 1; + } + puVar7 = local_b8; + for (lVar4 = 0x14; lVar4 != 0; lVar4 = lVar4 + -1) { + *puVar7 = 0; + puVar7 = puVar7 + 1; + } + if (param_1 < 2) { + uVar2 = 0x29; + } + else { + uVar2 = strtoull(*(char **)(param_2 + 8),(char **)0x0,0); + } + if (param_1 < 3) { + uVar3 = 6; + } + else { + uVar3 = strtoull(*(char **)(param_2 + 0x10),(char **)0x0,0); + } + for (local_184 = 0; local_184 < 0x18; local_184 = local_184 + 1) { + uVar5 = ((long)local_184 * 0x13 + uVar2) % 0x500; + uVar6 = (uVar3 + uVar5 + 0xb) % 0x500; + vmsave(local_158,uVar5); + if ((((uint)uVar5 ^ (uint)uVar3) & 3) == 0) { + vmsave(local_b8,uVar6); + } + } + local_180 = uVar2 % 0x500; + unique0x10000534 = vmread(local_158,local_180); + bVar1 = (byte)local_180; + if (!unique0x10000534) { + vmsave(local_158,local_180); + local_180 = uVar3; + } + else { + vmclear(local_158,local_180); + } + uVar2 = (uVar2 * 7 + uVar3) % 0x500; + unique0x10000535 = vmread(local_b8,uVar2); + bVar1 = (byte)uVar2; + if (!unique0x10000535) { + if (vmread(local_158,uVar2)) { + vmsave(local_b8,uVar2); + local_180 = local_180 ^ uVar2 * 2; + } + } + else { + vmclear(local_b8,uVar2); + local_180 = local_180 + uVar2 * 3; + } + if ((uVar3 & 3) == 0) { + vmsave(local_158,local_180 % 0x500); + } + else if (vmread(local_158,uVar3 % 0x500)) { + local_180 = local_180 + 100; + } + printf("score=%llu users0=%llx locked3=%llx users19=%llx\n",local_180,local_158[0],local_b8[3], + local_c0); + if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { + /* WARNING: Subroutine does not return */ + __stack_chk_fail(); + } + return (uint)local_180 & 7; +} + + diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/100_word b/src/plugin/tests/fixtures/04_bitmap/rules/100_word new file mode 100644 index 0000000..5401a09 --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/100_word @@ -0,0 +1,6 @@ +v_bit-> (0) o_shift(INT_RIGHT, _) -> v_res; +#6 -> (1)o_shift +-- +#120 ->>(0) o_word(USERDEFINED BEFORE o_shift) ->> v_res; +v_bit ->>(1) o_word; +DELETE o_shift \ No newline at end of file diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/200_shift b/src/plugin/tests/fixtures/04_bitmap/rules/200_shift new file mode 100644 index 0000000..1dbbf1a --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/200_shift @@ -0,0 +1,7 @@ +#0 -> (1) o_to_byte(SUBPIECE, _); +v_bit ->(0) o_to_byte -> v_bit_1-> (0) o_and(INT_AND, _) -> v_res; +#0x3f -> (1)o_and +-- +#121 ->>(0) o_shift(USERDEFINED BEFORE o_and) ->> v_res; +v_bit ->>(1) o_shift; +DELETE o_and \ No newline at end of file diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/300_mask b/src/plugin/tests/fixtures/04_bitmap/rules/300_mask new file mode 100644 index 0000000..bb9d4e7 --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/300_mask @@ -0,0 +1,8 @@ +#1 -> (0) o_lft(INT_LEFT, _) -> v_res; +#121 ->(0) o_shift(USERDEFINED, _) -> v_shift_res -> (1)o_lft; +v_bit ->(1) o_shift + +-- +#117 ->>(0) o_mask(USERDEFINED BEFORE o_lft) ->> v_res; +v_bit ->>(1) o_mask; +DELETE o_lft diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/330_mask_reorder_or b/src/plugin/tests/fixtures/04_bitmap/rules/330_mask_reorder_or new file mode 100644 index 0000000..710fb3b --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/330_mask_reorder_or @@ -0,0 +1,8 @@ +#117 ->(0) o_mask(USERDEFINED, _) -> v_res ->(1) o_or(INT_OR, _); +v_other ->(0) o_or(INT_OR, _) + +-- + +v_other ->>(1) o_or; +v_res ->>(0) o_or + diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/400_set b/src/plugin/tests/fixtures/04_bitmap/rules/400_set new file mode 100644 index 0000000..e482a26 --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/400_set @@ -0,0 +1,17 @@ +#117 ->(0) o_mask(USERDEFINED,_) -> v_mask_res; +v_bit ->(1) o_mask; + + v_map ->(0) o_addr; +#0x78 ->(0) o_word(USERDEFINED, _) -> v_word_res ->(1) o_addr -> vtmp ->(1) o_load(LOAD, _)-> v_mask_map; +v_bit ->(1) o_word; + +v_mask_res ->(0) o_or(INT_OR, _); +v_mask_map ->(1) o_or -> v_or_res ->(2) o_finstore(STORE, _) +-- +#26 ->>(0) o_set(USERDEFINED BEFORE o_mask); +v_map ->>(1) o_set; +v_bit ->>(2) o_set; + +DELETE o_finstore + + diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/410_set_alt b/src/plugin/tests/fixtures/04_bitmap/rules/410_set_alt new file mode 100644 index 0000000..8b20d09 --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/410_set_alt @@ -0,0 +1,17 @@ +#117 ->(0) o_mask(USERDEFINED,_) -> v_mask_res; +v_bit1 ->(1) o_mask; + + v_map ->(0) o_addr; +#0x78 ->(0) o_word(USERDEFINED, _) -> v_word_res ->(1) o_addr -> vtmp ->(1) o_load(LOAD, _)-> v_mask_map; +v_bit ->(1) o_word; + +v_mask_res ->(0) o_or(INT_OR, _); +v_mask_map ->(1) o_or -> v_or_res ->(2) o_finstore(STORE, _) +-- +#26 ->>(0) o_set(USERDEFINED BEFORE o_mask); +v_map ->>(1) o_set; +v_bit ->>(2) o_set; + +DELETE o_finstore + + diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/500_clear b/src/plugin/tests/fixtures/04_bitmap/rules/500_clear new file mode 100644 index 0000000..6c9ce9d --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/500_clear @@ -0,0 +1,18 @@ +#117 ->(0) o_mask(USERDEFINED,_) -> v_mask_res ->(0) o_inv(INT_NEGATE, _) -> v_mask_neg; +v_bit ->(1) o_mask; + + v_map ->(0) o_addr; +#0x78 ->(0) o_word(USERDEFINED, _) -> v_word_res ->(1) o_addr -> vtmp ->(1) o_load(LOAD, _)-> v_mask_map; +v_bit ->(1) o_word; + +v_mask_map ->(0) o_and -> v_and_res ->(2) o_finstore(STORE, _); +v_mask_neg ->(1) o_and(INT_AND, _) + +-- +#30 ->>(0) o_set(USERDEFINED BEFORE o_mask); +v_map ->>(1) o_set; +v_bit ->>(2) o_set; + +DELETE o_finstore + + diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/600_pre_test b/src/plugin/tests/fixtures/04_bitmap/rules/600_pre_test new file mode 100644 index 0000000..121b2dc --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/600_pre_test @@ -0,0 +1,22 @@ + + v_map ->(0) o_addr; +#0x78 ->(0) o_word(USERDEFINED, _) -> v_word_res ->(1) o_addr -> vtmp ->(1) o_load(LOAD, _)-> v_mask_map; +v_bit ->(1) o_word; + + +#121 ->(0) o_shift(USERDEFINED, _) -> v_shift_res; +v_bit ->(1) o_shift; + +v_mask_map ->(0) o_right(INT_RIGHT, _) -> v_right_res; +v_shift_res ->(1) o_right; + +v_right_res ->(0) o_and(INT_AND, _) -> v_and_res ->(0) o_some; +#1 ->(1) o_and + + +-- +DELETE o_and; + +#33 ->>(0) o_pretest(USERDEFINED BEFORE o_word) ->> v_and_res; +v_map ->>(1) o_pretest; +v_bit ->>(2) o_pretest \ No newline at end of file diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/600_pre_test_alt b/src/plugin/tests/fixtures/04_bitmap/rules/600_pre_test_alt new file mode 100644 index 0000000..30d55a0 --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/600_pre_test_alt @@ -0,0 +1,22 @@ + + v_map ->(0) o_addr; +#0x78 ->(0) o_word(USERDEFINED, _) -> v_word_res ->(1) o_addr -> vtmp ->(1) o_load(LOAD, _)-> v_mask_map; +v_bit ->(1) o_word; + + +#121 ->(0) o_shift(USERDEFINED, _) -> v_shift_res; +v_bit1 ->(1) o_shift; + +v_mask_map ->(0) o_right(INT_RIGHT, _) -> v_right_res; +v_shift_res ->(1) o_right; + +v_right_res ->(0) o_and(INT_AND, _) -> v_and_res ->(0) o_some; +#1 ->(1) o_and + + +-- +DELETE o_and; + +#33 ->>(0) o_pretest(USERDEFINED BEFORE o_word) ->> v_and_res; +v_map ->>(1) o_pretest; +v_bit ->>(2) o_pretest \ No newline at end of file diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/610_test b/src/plugin/tests/fixtures/04_bitmap/rules/610_test new file mode 100644 index 0000000..8a59340 --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/610_test @@ -0,0 +1,17 @@ +#33 ->(0) o_pretest(USERDEFINED, _) -> v_pre_test_res; +v_map ->(1) o_pretest; +v_bit ->(2) o_pretest; + +v_pre_test_res ->(0) o_cond(INT_NOTEQUAL, _) -> v_cond_res; +#0 ->(1) o_cond + + +-- +#36 ->>(0) o_test(USERDEFINED BEFORE o_pretest) ->> v_cond_res; +v_map ->>(1) o_test; +v_bit ->>(2) o_test; + + + +DELETE o_cond + diff --git a/src/plugin/tests/fixtures/04_bitmap/rules/610_test_alt b/src/plugin/tests/fixtures/04_bitmap/rules/610_test_alt new file mode 100644 index 0000000..6073d61 --- /dev/null +++ b/src/plugin/tests/fixtures/04_bitmap/rules/610_test_alt @@ -0,0 +1,16 @@ +#33 ->(0) o_pretest(USERDEFINED, _) -> v_pre_test_res; +v_map ->(1) o_pretest; +v_bit ->(2) o_pretest; + +v_pre_test_res ->(0) o_cond(INT_EQUAL, _) -> v_cond_res; +#0 ->(1) o_cond + + +-- +#36 ->>(0) o_test(USERDEFINED AFTER o_pretest) ->> v_test_res ->>(0) o_inv(BOOL_NEGATE AFTER o_pretest) ->> v_cond_res; +v_map ->>(1) o_test; +v_bit ->>(2) o_test; + + +DELETE o_cond + diff --git a/src/plugin/tests/fixtures/04_bitmap/target/bitmap.elf b/src/plugin/tests/fixtures/04_bitmap/target/bitmap.elf new file mode 100755 index 0000000..acf1049 Binary files /dev/null and b/src/plugin/tests/fixtures/04_bitmap/target/bitmap.elf differ diff --git a/src/plugin/tests/meson.build b/src/plugin/tests/meson.build new file mode 100644 index 0000000..9bfd2d9 --- /dev/null +++ b/src/plugin/tests/meson.build @@ -0,0 +1,46 @@ +project( + 'pcode-weaver-plugin-system-tests', + 'cpp', + meson_version: '>=1.10.0', + default_options: [ + 'cpp_std=c++17', + ], +) + +approvaltests_dep = dependency( + 'approvaltests', + fallback: ['approvaltests', 'approvaltests_dep'], +) + +plugin_approval_test = executable( + 'plugin_approval_test', + 'plugin_approval_test.cpp', + cpp_args: ['-DAPPROVAL_TESTS_DISABLE_FILE_MACRO_CHECK'], + dependencies: [ + approvaltests_dep, + ], +) + +cases_find = run_command( + 'find', + 'fixtures', + '-mindepth', + '1', + '-maxdepth', + '1', + '-type', + 'd', + check: true, +) + +cases = cases_find.stdout().strip().split('\n') + +foreach case_dir : cases + test( + 'plugin / ' + case_dir.split('/')[-1], + plugin_approval_test, + args: [case_dir], + workdir: meson.current_source_dir(), + timeout: 600, + ) +endforeach diff --git a/src/plugin/tests/plugin_approval_test.cpp b/src/plugin/tests/plugin_approval_test.cpp new file mode 100644 index 0000000..83e7a29 --- /dev/null +++ b/src/plugin/tests/plugin_approval_test.cpp @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: © 2026 Efremov Alexey <4osogi@gmail.com> + +#include "ApprovalTests/ApprovalTests.hpp" +#include "ApprovalTests/core/ApprovalNamer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +namespace fs = std::filesystem; + +class CaseNamer : public ApprovalTests::ApprovalNamer { +public: + explicit CaseNamer(fs::path caseDir) : caseDir(std::move(caseDir)) {} + + std::string getApprovedFile(std::string) const override { + return (caseDir / "approved" / "main.approved.txt").string(); + } + + std::string getReceivedFile(std::string) const override { + return (caseDir / "approved" / "main.received.txt").string(); + } + +private: + fs::path caseDir; +}; + +std::string shellQuote(const fs::path &path) { + std::string input = path.string(); + std::string quoted = "'"; + for (const char ch : input) { + if (ch == '\'') { + quoted += "'\\''"; + } else { + quoted += ch; + } + } + quoted += "'"; + return quoted; +} + +std::string shellQuote(const std::string &input) { + std::string quoted = "'"; + for (const char ch : input) { + if (ch == '\'') { + quoted += "'\\''"; + } else { + quoted += ch; + } + } + quoted += "'"; + return quoted; +} + +std::string getenvOr(std::string_view name, std::string fallback) { + const char *value = std::getenv(std::string(name).c_str()); + if (value == nullptr || *value == '\0') { + return fallback; + } + return value; +} + +std::string runCase(const fs::path &caseDir) { + const fs::path script = fs::current_path() / "scripts" / "run-docker-case.sh"; + const std::string image = getenvOr( + "PCODE_WEAVER_PLUGIN_TEST_IMAGE", "pcode-weaver-plugin-test:latest" + ); + const std::string command = shellQuote(script) + " " + + shellQuote(fs::absolute(caseDir)) + " " + + shellQuote(image); + + std::array buffer{}; + std::string output; + + FILE *pipe = popen(command.c_str(), "r"); + if (pipe == nullptr) { + throw std::runtime_error("failed to start plugin test runner"); + } + + while (fgets(buffer.data(), static_cast(buffer.size()), pipe) != + nullptr) { + output += buffer.data(); + } + + const int status = pclose(pipe); + if (status == -1) { + throw std::runtime_error("failed to close plugin test runner"); + } + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + throw std::runtime_error( + "plugin test runner failed for " + caseDir.string() + ); + } + + return output; +} + +} // namespace + +int main(int argc, char **argv) { + if (argc != 2) { + std::cerr << "usage: " << argv[0] << " \n"; + return EXIT_FAILURE; + } + + const fs::path caseDir = argv[1]; + + try { + static const ApprovalTests::QuietReporter quietReporter; + ApprovalTests::Approvals::verify( + runCase(caseDir), + ApprovalTests::Options() + .withReporter(quietReporter) + .withNamer(std::make_shared(caseDir)) + ); + } catch (const std::exception &ex) { + std::cerr << ex.what() << "\n"; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/plugin/tests/scripts/DecompileFunction.java b/src/plugin/tests/scripts/DecompileFunction.java new file mode 100644 index 0000000..de5f3e7 --- /dev/null +++ b/src/plugin/tests/scripts/DecompileFunction.java @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: © 2026 Efremov Alexey <4osogi@gmail.com> + +import ghidra.app.decompiler.DecompInterface; +import ghidra.app.decompiler.DecompileResults; +import ghidra.app.script.GhidraScript; +import ghidra.program.model.listing.Function; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public class DecompileFunction extends GhidraScript { + @Override + public void run() throws Exception { + String[] args = getScriptArgs(); + String functionName = args.length > 0 ? args[0] : "main"; + Path outputPath = Path.of(args.length > 1 ? args[1] : "decompiled.c"); + + List functions = getGlobalFunctions(functionName); + if (functions.isEmpty()) { + throw new IllegalStateException("function not found: " + functionName); + } + + DecompInterface decompiler = new DecompInterface(); + decompiler.openProgram(currentProgram); + DecompileResults results = + decompiler.decompileFunction(functions.get(0), 120, monitor); + if (!results.decompileCompleted()) { + throw new IllegalStateException( + "decompile failed: " + results.getErrorMessage() + ); + } + + Files.writeString(outputPath, results.getDecompiledFunction().getC()); + decompiler.dispose(); + } +} diff --git a/src/plugin/tests/scripts/run-case.sh b/src/plugin/tests/scripts/run-case.sh new file mode 100755 index 0000000..0f5b657 --- /dev/null +++ b/src/plugin/tests/scripts/run-case.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ne 2 ]]; then + echo "usage: $0 " >&2 + exit 2 +fi + +case_dir=$1 +rules_dir=$2 +work_dir=$(mktemp -d) +project_dir="${work_dir}/ghidra-project" +output_file="${work_dir}/main.c" +reoxide_log="${work_dir}/reoxided.log" +ghidra_log="${work_dir}/ghidra.log" +ghidra_timeout_seconds="${GHIDRA_TIMEOUT_SECONDS:-240}" + +cleanup() { + if [[ -n "${reoxide_pid:-}" ]]; then + kill "${reoxide_pid}" >/dev/null 2>&1 || true + wait "${reoxide_pid}" >/dev/null 2>&1 || true + fi + rm -rf "${work_dir}" +} +trap cleanup EXIT + +mkdir -p "${project_dir}" + +mapfile -t binaries < <(find "${case_dir}/target" -maxdepth 1 -type f -name '*.elf' | sort) +if [[ ${#binaries[@]} -ne 1 ]]; then + echo "expected exactly one ELF in ${case_dir}/target, found ${#binaries[@]}" >&2 + exit 1 +fi + +export PCODE_WEAVER_RULE_DIR="${rules_dir}" +export REOXIDE_CONFIG=/opt/reoxide/reoxide.toml +export REOXIDE_BIND="ipc://${work_dir}/reoxide.sock" +export REOXIDE_HOST="${REOXIDE_BIND}" +export REOXIDE_MANAGE_BIND="ipc://${work_dir}/reoxide-manage.sock" +export REOXIDE_MANAGE_HOST="${REOXIDE_MANAGE_BIND}" + +echo "plugin-test: starting reoxided" >&2 +reoxided -c "${REOXIDE_CONFIG}" -b "${REOXIDE_BIND}" -m "${REOXIDE_MANAGE_BIND}" \ + >"${reoxide_log}" 2>&1 & +reoxide_pid=$! + +reoxide_ready=false +for _ in $(seq 1 50); do + if [[ -e "${work_dir}/reoxide-manage.sock" ]]; then + reoxide_ready=true + break + fi + if ! kill -0 "${reoxide_pid}" >/dev/null 2>&1; then + break + fi + sleep 0.1 +done + +if [[ "${reoxide_ready}" != true ]]; then + echo "reoxided did not become ready" >&2 + cat "${reoxide_log}" >&2 + exit 1 +fi + +if ! kill -0 "${reoxide_pid}" >/dev/null 2>&1; then + echo "reoxided exited before Ghidra started" >&2 + cat "${reoxide_log}" >&2 + exit 1 +fi + +echo "plugin-test: running Ghidra headless decompile for ${binaries[0]}" >&2 +if ! timeout "${ghidra_timeout_seconds}s" "${GHIDRA_INSTALL_DIR}/support/analyzeHeadless" \ + "${project_dir}" pcode-weaver-plugin-test \ + -import "${binaries[0]}" \ + -scriptPath /opt/pcode-weaver/scripts \ + -postScript DecompileFunction.java main "${output_file}" \ + -deleteProject \ + >"${ghidra_log}" 2>&1; then + echo "Ghidra headless run failed or timed out after ${ghidra_timeout_seconds}s" >&2 + echo "----- ghidra.log -----" >&2 + cat "${ghidra_log}" >&2 + echo "----- reoxided.log -----" >&2 + cat "${reoxide_log}" >&2 + exit 1 +fi + +echo "plugin-test: decompile finished" >&2 +cat "${output_file}" diff --git a/src/plugin/tests/scripts/run-docker-case.sh b/src/plugin/tests/scripts/run-docker-case.sh new file mode 100755 index 0000000..10359fc --- /dev/null +++ b/src/plugin/tests/scripts/run-docker-case.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -lt 1 || $# -gt 2 ]]; then + echo "usage: $0 [docker-image]" >&2 + exit 2 +fi + +case_dir=$(realpath "$1") +image="${2:-${PCODE_WEAVER_PLUGIN_TEST_IMAGE:-pcode-weaver-plugin-test:latest}}" +repo_root=$(git -C "$(dirname "$0")" rev-parse --show-toplevel) +compiler="${repo_root}/src/compiler/build/bin/compiler.elf" +work_dir=$(mktemp -d) +rules_dir="${work_dir}/compiled-rules" + +cleanup() { + rm -rf "${work_dir}" +} +trap cleanup EXIT + +if [[ ! -x "${compiler}" ]]; then + echo "compiler not found or not executable: ${compiler}" >&2 + echo "build it first from src/compiler with: meson compile -j 1 -C build" >&2 + exit 1 +fi + +mapfile -t rules < <(find "${case_dir}/rules" -maxdepth 1 -type f | sort) +if [[ ${#rules[@]} -eq 0 ]]; then + echo "no rules found in ${case_dir}/rules" >&2 + exit 1 +fi + +mkdir -p "${rules_dir}" +"${compiler}" -d "${rules_dir}" "${rules[@]}" >&2 + +docker run --rm \ + --volume "${case_dir}:/case:ro" \ + --volume "${rules_dir}:/compiled-rules:ro" \ + "${image}" \ + /opt/pcode-weaver/scripts/run-case.sh /case /compiled-rules diff --git a/src/plugin/tests/subprojects/approvaltests.wrap b/src/plugin/tests/subprojects/approvaltests.wrap new file mode 100644 index 0000000..13e345a --- /dev/null +++ b/src/plugin/tests/subprojects/approvaltests.wrap @@ -0,0 +1,9 @@ +[wrap-git] +url = https://github.com/approvals/ApprovalTests.cpp.git +revision = v.10.13.0 +depth = 1 +method = meson +patch_directory = approvaltests + +[provide] +approvaltests = approvaltests_dep diff --git a/src/plugin/tests/subprojects/packagefiles/approvaltests/meson.build b/src/plugin/tests/subprojects/packagefiles/approvaltests/meson.build new file mode 100644 index 0000000..fdd1af8 --- /dev/null +++ b/src/plugin/tests/subprojects/packagefiles/approvaltests/meson.build @@ -0,0 +1,42 @@ +project( + 'approvaltests', + 'cpp', + version: '10.13.0', +) + +approvaltests_sources_text = run_command( + 'find', + 'ApprovalTests', + '-name', + '*.cpp', + check: true, +).stdout().strip() + +approvaltests_sources = [] +if approvaltests_sources_text != '' + approvaltests_sources = approvaltests_sources_text.split('\n') +endif + +approvaltests_inc = [ + include_directories('.'), + include_directories('ApprovalTests'), +] + +approvaltests_dep = declare_dependency( + include_directories: approvaltests_inc, +) + +if approvaltests_sources.length() != 0 + approvaltests_lib = static_library( + 'approvaltests', + approvaltests_sources, + include_directories: approvaltests_inc, + ) + + approvaltests_dep = declare_dependency( + include_directories: approvaltests_inc, + link_with: approvaltests_lib, + ) +endif + +meson.override_dependency('approvaltests', approvaltests_dep)