├── .clang-format ├── .gitattributes ├── .github └── workflows │ ├── anghabench-after-build.yml │ ├── anghabench-cron-jobs.yml │ ├── ci.yml │ └── diff_tests.yml ├── .gitignore ├── .gitmodules ├── ACKNOWLEDGEMENTS.md ├── CMakeLists.txt ├── CMakePresets.json ├── CODEOWNERS ├── Dockerfile ├── LICENSE ├── README.md ├── ci └── angha_1k_test_settings.json ├── cmake ├── git_watcher.cmake ├── modules │ ├── FindZ3.cmake │ ├── Findgflags.cmake │ ├── Findglog.cmake │ └── utils.cmake ├── options.cmake ├── packaging.cmake └── settings.cmake ├── docs ├── HelpingJohnny.pdf └── NoMoreGotos.pdf ├── include └── rellic │ ├── AST │ ├── ASTBuilder.h │ ├── ASTPass.h │ ├── CXXToCDecl.h │ ├── CondBasedRefine.h │ ├── DeadStmtElim.h │ ├── DebugInfoCollector.h │ ├── DecompilationContext.h │ ├── ExprCombine.h │ ├── GenerateAST.h │ ├── IRToASTVisitor.h │ ├── InferenceRule.h │ ├── LocalDeclRenamer.h │ ├── LoopRefine.h │ ├── MaterializeConds.h │ ├── NestedCondProp.h │ ├── NestedScopeCombine.h │ ├── ReachBasedRefine.h │ ├── StructFieldRenamer.h │ ├── StructGenerator.h │ ├── SubprogramGenerator.h │ ├── TransformVisitor.h │ ├── TypeProvider.h │ ├── Util.h │ └── Z3CondSimplify.h │ ├── BC │ ├── Util.h │ └── Version.h │ ├── Dec2Hex.h │ ├── Decompiler.h │ ├── Exception.h │ ├── Result.h │ └── Version.h ├── lib ├── AST │ ├── ASTBuilder.cpp │ ├── CXXToCDecl.cpp │ ├── CondBasedRefine.cpp │ ├── DeadStmtElim.cpp │ ├── DebugInfoCollector.cpp │ ├── ExprCombine.cpp │ ├── GenerateAST.cpp │ ├── IRToASTVisitor.cpp │ ├── InferenceRule.cpp │ ├── LocalDeclRenamer.cpp │ ├── LoopRefine.cpp │ ├── MaterializeConds.cpp │ ├── NestedCondProp.cpp │ ├── NestedScopeCombine.cpp │ ├── ReachBasedRefine.cpp │ ├── StructFieldRenamer.cpp │ ├── StructGenerator.cpp │ ├── SubprogramGenerator.cpp │ ├── TypeProvider.cpp │ ├── Util.cpp │ └── Z3CondSimplify.cpp ├── BC │ └── Util.cpp ├── CMakeLists.txt ├── Dec2Hex.cpp ├── Decompiler.cpp ├── Exception.cpp └── Version.cpp.in ├── packaging ├── README.md ├── cmake │ ├── dispatcher.cmake │ └── system │ │ ├── linux │ │ └── generators │ │ │ ├── deb.cmake │ │ │ ├── rpm.cmake │ │ │ └── tgz.cmake │ │ └── macos │ │ └── generators │ │ └── tgz.cmake └── main.cmake ├── rellicConfig.cmake.in ├── scripts ├── build-preset.sh ├── build.sh ├── decompile.py ├── docker-decomp-entrypoint.sh ├── generate_changelog.sh ├── requirements.txt ├── roundtrip.py ├── run-on-anghabench.sh ├── test-angha-1k.sh └── test-headergen.py ├── tests └── tools │ ├── decomp │ ├── array_swap.c │ ├── assert.c │ ├── binops.c │ ├── bitmask.c │ ├── bitops.c │ ├── bool.c │ ├── branch.c │ ├── byval_struct.c │ ├── byval_tail_gep.ll │ ├── byval_tail_nogep.ll │ ├── cast.c │ ├── conflicting_global.c │ ├── conflicting_names.c │ ├── diff_outputs.mk │ ├── failing-rebuild │ │ ├── README.md │ │ ├── chal-1.c │ │ └── issue_333_inline_asm.c │ ├── fcmp.c │ ├── fizzbuzz.c │ ├── fizzbuzz_stateful.c │ ├── float.c │ ├── func_cond_two_arg.c │ ├── func_cond_zero_arg.c │ ├── funcptr.c │ ├── global_using_function_decl.c │ ├── goto_loop.c │ ├── init_list.c │ ├── inttoptr.c │ ├── issue_123_uint128_t.c │ ├── issue_127_uint128_t_lit.c │ ├── issue_183_literal_structs.c │ ├── issue_335_z3_ite.ll │ ├── issue_4.c │ ├── issue_94_strncmp.c │ ├── known-failures │ │ └── issue_126_bool2bv.c │ ├── loop.c │ ├── nested_struct.c │ ├── nested_while.c │ ├── nullptr.c │ ├── reg_test_structure_fields.c │ ├── ret0.c │ ├── short.c │ ├── struct.c │ ├── struct_swap.c │ ├── switch.c │ ├── switch_loop.c │ ├── template_parameter_pack.cpp │ ├── trunc.c │ ├── typedefs_of_typedefs.c │ ├── vectors.c │ ├── zeroinit.c │ └── zext.c │ └── headergen │ ├── alias.cpp │ ├── alignment.c │ ├── anon_union.c │ ├── atomic.c │ ├── bigstruct.cpp │ ├── bitfield.c │ ├── enum_basetype.cpp │ ├── fwddecl.cpp │ ├── inheritance.cpp │ ├── multiple_bases.cpp │ ├── packed.c │ ├── packed_bitfield.c │ ├── packed_bitfield_mix.c │ ├── static_member.cpp │ ├── union.c │ ├── unordered_map.cpp │ ├── vector.cpp │ └── vector_string.cpp ├── tools ├── CMakeLists.txt ├── dec2hex │ └── dec2hex.cpp ├── decomp │ └── Decomp.cpp ├── headergen │ ├── HeaderGen.cpp │ └── README.md ├── plugins │ └── ida-rellic.py ├── repl │ └── Repl.cpp └── xref │ ├── DeclPrinter.cpp │ ├── Printer.h │ ├── README.md │ ├── StmtPrinter.cpp │ ├── TypePrinter.cpp │ ├── Xref.cpp │ └── www │ ├── index.html │ ├── main.js │ └── style.css └── unittests ├── AST ├── ASTBuilder.cpp ├── StructGenerator.cpp ├── Util.cpp ├── Util.h └── Z3ConvVisitor.cpp ├── CMakeLists.txt └── UnitTest.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | SpaceBeforeParens: ControlStatements 5 | ... 6 | 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.sh eol=lf 4 | -------------------------------------------------------------------------------- /.github/workflows/diff_tests.yml: -------------------------------------------------------------------------------- 1 | name: Diff test outputs 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '*' 7 | 8 | jobs: 9 | do-the-job: 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | image: 14 | - { name: 'ubuntu', tag: '20.04', codename: 'focal' } 15 | llvm: [ '16' ] 16 | common_base: [ 'https://github.com/lifting-bits/cxx-common/releases/download/v0.4.1' ] 17 | 18 | env: 19 | CC: clang-${{ matrix.llvm }} 20 | CXX: clang++-${{ matrix.llvm }} 21 | 22 | name: Diff in ouput between old and new rellic 23 | runs-on: gha-ubuntu-32 24 | container: 25 | image: ghcr.io/lifting-bits/cxx-common/vcpkg-builder-${{ matrix.image.name }}-v2:${{ matrix.image.tag }} 26 | credentials: 27 | username: ${{ github.actor }} 28 | password: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | steps: 31 | - name: Adding github workspace as safe directory 32 | # See issue https://github.com/actions/checkout/issues/760 33 | run: git config --global --add safe.directory $GITHUB_WORKSPACE 34 | - name: Fetch merge 35 | uses: actions/checkout@v3 36 | with: 37 | fetch-depth: 0 38 | submodules: true 39 | - name: Fetch base branch 40 | uses: actions/checkout@v3 41 | with: 42 | ref: ${{ github.base_ref }} 43 | fetch-depth: 0 44 | submodules: true 45 | path: old 46 | - name: Install utility tools 47 | shell: bash 48 | run: | 49 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 50 | echo "deb http://apt.llvm.org/${{ matrix.image.codename }}/ llvm-toolchain-${{ matrix.image.codename }}-${{ matrix.llvm }} main" >> /etc/apt/sources.list 51 | echo "deb-src http://apt.llvm.org/${{ matrix.image.codename }}/ llvm-toolchain-${{ matrix.image.codename }}-${{ matrix.llvm }} main" >> /etc/apt/sources.list 52 | apt-get update 53 | apt-get install -y ninja-build pixz xz-utils make rpm python3.8 clang-${{ matrix.llvm }} 54 | update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 100 55 | - name: Download cxx-commons 56 | shell: bash 57 | run: | 58 | curl ${{ matrix.common_base }}/vcpkg_${{ matrix.image.name }}-${{ matrix.image.tag }}_llvm-${{ matrix.llvm }}_amd64.tar.xz \ 59 | -L -o vcpkg_${{ matrix.image.name }}-${{ matrix.image.tag }}_llvm-${{ matrix.llvm }}_amd64.tar.xz 60 | tar xf vcpkg_${{ matrix.image.name }}-${{ matrix.image.tag }}_llvm-${{ matrix.llvm }}_amd64.tar.xz 61 | 62 | - name: Build old rellic 63 | shell: bash 64 | run: | 65 | cmake -G Ninja -S old -B rellic-build-old -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/vcpkg_${{ matrix.image.name }}-${{ matrix.image.tag }}_llvm-${{ matrix.llvm }}_amd64/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET="x64-linux-rel" 66 | cmake --build rellic-build-old 67 | 68 | - name: Build new rellic 69 | shell: bash 70 | run: | 71 | cmake -G Ninja -S . -B rellic-build -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/vcpkg_${{ matrix.image.name }}-${{ matrix.image.tag }}_llvm-${{ matrix.llvm }}_amd64/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET="x64-linux-rel" 72 | cmake --build rellic-build 73 | 74 | - name: Print job summary 75 | shell: bash 76 | run: | 77 | echo "# Test diffs" >> $GITHUB_STEP_SUMMARY 78 | cd $GITHUB_WORKSPACE/tests/tools/decomp 79 | env CLANG=clang-${{ matrix.llvm }} \ 80 | OLD_RELLIC=$GITHUB_WORKSPACE/rellic-build-old/tools/rellic-decomp \ 81 | NEW_RELLIC=$GITHUB_WORKSPACE/rellic-build/tools/rellic-decomp \ 82 | make -s -j1 -f diff_outputs.mk >> $GITHUB_STEP_SUMMARY 83 | 84 | - name: Output generated markdown 85 | shell: bash 86 | id: md 87 | run: | 88 | cd $GITHUB_WORKSPACE/tests/tools/decomp 89 | env CLANG=clang-${{ matrix.llvm }} \ 90 | OLD_RELLIC=$GITHUB_WORKSPACE/rellic-build-old/tools/rellic-decomp \ 91 | NEW_RELLIC=$GITHUB_WORKSPACE/rellic-build/tools/rellic-decomp \ 92 | make -s -j1 -f diff_outputs.mk >> $GITHUB_WORKSPACE/test-diff.md 93 | 94 | - name: Add comment 95 | uses: actions/github-script@v6 96 | with: 97 | script: | 98 | const fs = require('fs') 99 | const body = fs.readFileSync('test-diff.md', {encoding:'utf-8'}) 100 | const message = `See the diff generated by this PR for the tests here: https://github.com/lifting-bits/rellic/actions/runs/${{ github.run_id }} 101 |
102 | 103 | ${body} 104 | 105 |
` 106 | 107 | github.rest.issues.createComment({ 108 | issue_number: context.issue.number, 109 | owner: context.repo.owner, 110 | repo: context.repo.repo, 111 | body: message 112 | }) 113 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | install_manifest.txt 10 | Makefile 11 | 12 | CMakeLists.txt.user 13 | .vscode 14 | .idea 15 | .cache 16 | cmake-build-debug 17 | cmake-build-release 18 | compile_commands.json 19 | 20 | third_party/* 21 | build/* 22 | generated/* 23 | 24 | tools/build/* 25 | 26 | rellic-build* 27 | rellic-angha-test-1k 28 | 29 | # LLVM bitcode files 30 | *.ll 31 | *.bc 32 | 33 | # Compiled Object files 34 | *.slo 35 | *.lo 36 | *.o 37 | *.obj 38 | 39 | # Precompiled Headers 40 | *.gch 41 | *.pch 42 | 43 | # Compiled Dynamic libraries 44 | *.so 45 | *.dylib 46 | *.dll 47 | 48 | # Compiled Static libraries 49 | *.lai 50 | *.la 51 | *.a 52 | *.lib 53 | 54 | # Executables 55 | *.exe 56 | *.out 57 | *.app 58 | 59 | *.pyc 60 | 61 | # VIM 62 | *.swp 63 | 64 | # Diff files from testing 65 | *.diff -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/lifting-tools-ci"] 2 | path = external/lifting-tools-ci 3 | url = https://github.com/lifting-bits/lifting-tools-ci.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /ACKNOWLEDGEMENTS.md: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | 3 | ## Individuals 4 | 5 | The following individuals have graciously contributed their time to improving 6 | Rellic: 7 | 8 | - [Alessandro Gario](https://github.com/alessandrogario) 9 | - [Peter Goodman](https://github.com/pgoodman) 10 | - [Marek Surovič](https://github.com/surovic) 11 | 12 | ## Companies 13 | 14 | The following companies have graciously supported the development of Rellic: 15 | 16 | - [Trail of Bits](https://github.com/trailofbits) 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | cmake_minimum_required(VERSION 3.21) 10 | 11 | include("cmake/options.cmake") 12 | 13 | project(rellic) 14 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 15 | set(CMAKE_C_VISIBILITY_PRESET hidden) 16 | set (VISIBILITY_INLINES_HIDDEN YES) 17 | include("cmake/settings.cmake") 18 | include(GNUInstallDirs) 19 | 20 | if(RELLIC_ENABLE_INSTALL) 21 | include("cmake/packaging.cmake") 22 | endif(RELLIC_ENABLE_INSTALL) 23 | 24 | enable_language(C CXX ASM) 25 | 26 | set(RELLIC_SOURCE_DIR "${PROJECT_SOURCE_DIR}") 27 | 28 | add_library("${PROJECT_NAME}_cxx_settings" INTERFACE) 29 | target_compile_features("${PROJECT_NAME}_cxx_settings" 30 | INTERFACE 31 | cxx_std_17 32 | ) 33 | 34 | # warnings and compiler settings 35 | if(NOT DEFINED WIN32) 36 | target_compile_options("${PROJECT_NAME}_cxx_settings" 37 | INTERFACE 38 | -Werror 39 | # -Wconversion 40 | -pedantic 41 | -Wno-unreachable-code-return 42 | ) 43 | endif(NOT DEFINED WIN32) 44 | 45 | # 46 | # libraries 47 | # 48 | 49 | find_package(gflags CONFIG REQUIRED) 50 | find_package(glog CONFIG REQUIRED) 51 | find_package(Z3 4.8 CONFIG REQUIRED) 52 | find_package(doctest CONFIG REQUIRED) 53 | find_package(LLVM CONFIG REQUIRED) 54 | llvm_map_components_to_libnames(llvm_libs support core irreader bitreader bitwriter) 55 | find_package(Clang CONFIG REQUIRED) 56 | 57 | 58 | # 59 | # helper macro to set target properties 60 | # 61 | 62 | if(RELLIC_ENABLE_INSTALL) 63 | export(PACKAGE "${PROJECT_NAME}") 64 | 65 | set(cmake_install_dir "lib/cmake/${PROJECT_NAME}") 66 | 67 | include(CMakePackageConfigHelpers) 68 | configure_package_config_file("${PROJECT_NAME}Config.cmake.in" 69 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 70 | INSTALL_DESTINATION "${cmake_install_dir}" 71 | ) 72 | 73 | install( 74 | FILES 75 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 76 | DESTINATION "${cmake_install_dir}" 77 | ) 78 | install(EXPORT "${PROJECT_NAME}Targets" 79 | DESTINATION "${cmake_install_dir}" 80 | NAMESPACE "${PROJECT_NAME}::" 81 | ) 82 | install( 83 | TARGETS 84 | "${PROJECT_NAME}_cxx_settings" 85 | EXPORT 86 | "${PROJECT_NAME}Targets" 87 | ) 88 | endif(RELLIC_ENABLE_INSTALL) 89 | 90 | # 91 | # rellic libraries 92 | # 93 | 94 | add_subdirectory(lib) 95 | 96 | # 97 | # rellic executables 98 | # 99 | 100 | add_subdirectory(tools) 101 | 102 | # 103 | # tests 104 | # 105 | 106 | 107 | if (RELLIC_ENABLE_TESTING) 108 | enable_testing() 109 | 110 | add_subdirectory(unittests) 111 | 112 | get_target_property(CLANG_PATH clang LOCATION) 113 | message(STATUS "Clang path for tests: \"${CLANG_PATH}\"") 114 | 115 | find_package(Python3 COMPONENTS Interpreter REQUIRED) 116 | message(STATUS "Python path for tests: \"${Python3_EXECUTABLE}\"") 117 | 118 | if(DEFINED CMAKE_OSX_SYSROOT) 119 | set(RELLIC_TEST_ARGS "--cflags=-isysroot" "--cflags=${CMAKE_OSX_SYSROOT}") 120 | else() 121 | set(RELLIC_TEST_ARGS "") 122 | endif() 123 | 124 | # Tests that survive a complete roundtrip 125 | add_test(NAME test_roundtrip_rebuild 126 | COMMAND "${Python3_EXECUTABLE}" scripts/roundtrip.py $ tests/tools/decomp/ "${CLANG_PATH}" --timeout 30 ${RELLIC_TEST_ARGS} 127 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 128 | ) 129 | 130 | # Tests that may not roundtrip yet, but should emit C 131 | add_test(NAME test_roundtrip_translate_only 132 | COMMAND "${Python3_EXECUTABLE}" scripts/roundtrip.py --translate-only $ tests/tools/decomp/failing-rebuild/ "${CLANG_PATH}" --timeout 30 ${RELLIC_TEST_ARGS} 133 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 134 | ) 135 | 136 | # Tests that can be decompiled 137 | add_test(NAME test_decompile 138 | COMMAND "${Python3_EXECUTABLE}" scripts/decompile.py $ tests/tools/decomp/ --timeout 30 139 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 140 | ) 141 | 142 | add_test(NAME test_headergen 143 | COMMAND "${Python3_EXECUTABLE}" scripts/test-headergen.py $ tests/tools/headergen/ "${CLANG_PATH}" ${RELLIC_TEST_ARGS} 144 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 145 | ) 146 | endif() 147 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @frabert 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Choose your LLVM version (only _some_ versions are supported) 2 | ARG LLVM_VERSION=16 3 | ARG UBUNTU_VERSION=22.04 4 | ARG DISTRO_BASE=ubuntu${UBUNTU_VERSION} 5 | ARG BUILD_BASE=ubuntu:${UBUNTU_VERSION} 6 | ARG LIBRARIES=/opt/trailofbits 7 | 8 | 9 | # Run-time dependencies go here 10 | FROM ${BUILD_BASE} as base 11 | 12 | # Build-time dependencies go here 13 | # See here for full list of those dependencies 14 | # https://github.com/lifting-bits/cxx-common/blob/master/docker/Dockerfile.ubuntu.vcpkg 15 | FROM ghcr.io/lifting-bits/cxx-common/vcpkg-builder-ubuntu-v2:${UBUNTU_VERSION} as deps 16 | ARG UBUNTU_VERSION 17 | ARG LLVM_VERSION 18 | ARG LIBRARIES 19 | 20 | RUN apt-get update && \ 21 | apt-get install -qqy python3 python3-pip libc6-dev wget liblzma-dev zlib1g-dev curl git build-essential ninja-build libselinux1-dev libbsd-dev ccache pixz xz-utils make rpm && \ 22 | if [ "$(uname -m)" = "x86_64" ]; then dpkg --add-architecture i386 && apt-get update && apt-get install -qqy gcc-multilib g++-multilib zip zlib1g-dev:i386; fi && \ 23 | rm -rf /var/lib/apt/lists/* 24 | 25 | # Source code build 26 | FROM deps as build 27 | ARG LLVM_VERSION 28 | ARG LIBRARIES 29 | ENV TRAILOFBITS_LIBRARIES="${LIBRARIES}" 30 | ENV PATH="${LIBRARIES}/llvm/bin/:${LIBRARIES}/cmake/bin:${PATH}" 31 | ENV CC=clang 32 | ENV CXX=clang++ 33 | 34 | WORKDIR /rellic 35 | COPY ./ ./ 36 | RUN ./scripts/build.sh \ 37 | --llvm-version ${LLVM_VERSION} \ 38 | --prefix /opt/trailofbits \ 39 | --extra-cmake-args "-DCMAKE_BUILD_TYPE=Release" \ 40 | --install 41 | 42 | # Small installation image 43 | FROM base as install 44 | ARG LLVM_VERSION 45 | 46 | COPY --from=build /opt/trailofbits /opt/trailofbits 47 | COPY scripts/docker-decomp-entrypoint.sh /opt/trailofbits 48 | ENV LLVM_VERSION=llvm${LLVM_VERSION} 49 | ENTRYPOINT ["/opt/trailofbits/docker-decomp-entrypoint.sh"] 50 | -------------------------------------------------------------------------------- /ci/angha_1k_test_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests.ignore": [ 3 | "amd64/SoftEtherVPN/src/See/extr_memory_t.h_SW_LONG_AT.bc", 4 | "arm64/SoftEtherVPN/src/See/extr_memory_t.h_SW_LONG_AT.bc", 5 | "armv7/SoftEtherVPN/src/See/extr_memory_t.h_SW_LONG_AT.bc", 6 | "x86/SoftEtherVPN/src/See/extr_memory_t.h_SW_LONG_AT.bc" 7 | ] 8 | } -------------------------------------------------------------------------------- /cmake/modules/FindZ3.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(CheckCXXSourceRuns) 2 | 3 | # Function to check Z3's version 4 | function(check_z3_version z3_include z3_lib) 5 | # Get lib path 6 | set(z3_link_libs "${z3_lib}") 7 | 8 | # Try to find a threading module in case Z3 was built with threading support. 9 | # Threads are required elsewhere in LLVM, but not marked as required here because 10 | # Z3 could have been compiled without threading support. 11 | find_package(Threads) 12 | # CMAKE_THREAD_LIBS_INIT may be empty if the thread functions are provided by the 13 | # system libraries and no special flags are needed. 14 | if(CMAKE_THREAD_LIBS_INIT) 15 | list(APPEND z3_link_libs "${CMAKE_THREAD_LIBS_INIT}") 16 | endif() 17 | 18 | # The program that will be executed to print Z3's version. 19 | file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testz3.cpp 20 | "#include 21 | #include 22 | int main() { 23 | unsigned int major, minor, build, rev; 24 | Z3_get_version(&major, &minor, &build, &rev); 25 | printf(\"%u.%u.%u\", major, minor, build); 26 | return 0; 27 | }") 28 | 29 | try_run( 30 | Z3_RETURNCODE 31 | Z3_COMPILED 32 | ${CMAKE_BINARY_DIR} 33 | ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testz3.cpp 34 | COMPILE_DEFINITIONS -I"${z3_include}" 35 | LINK_LIBRARIES ${z3_link_libs} 36 | COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT 37 | RUN_OUTPUT_VARIABLE SRC_OUTPUT 38 | ) 39 | 40 | if(Z3_COMPILED) 41 | string(REGEX REPLACE "([0-9]*\\.[0-9]*\\.[0-9]*)" "\\1" 42 | z3_version "${SRC_OUTPUT}") 43 | set(Z3_VERSION_STRING ${z3_version} PARENT_SCOPE) 44 | else() 45 | message(NOTICE "${COMPILE_OUTPUT}") 46 | message(WARNING "Failed to compile Z3 program that is used to determine library version.") 47 | endif() 48 | endfunction(check_z3_version) 49 | 50 | # Looking for Z3 in LLVM_Z3_INSTALL_DIR 51 | find_path(Z3_INCLUDE_DIR NAMES z3.h 52 | NO_DEFAULT_PATH 53 | PATHS ${LLVM_Z3_INSTALL_DIR}/include 54 | PATH_SUFFIXES libz3 z3 55 | ) 56 | 57 | find_library(Z3_LIBRARIES NAMES z3 libz3 58 | NO_DEFAULT_PATH 59 | PATHS ${LLVM_Z3_INSTALL_DIR} 60 | PATH_SUFFIXES lib bin 61 | ) 62 | 63 | # If Z3 has not been found in LLVM_Z3_INSTALL_DIR look in the default directories 64 | find_path(Z3_INCLUDE_DIR NAMES z3.h 65 | PATH_SUFFIXES libz3 z3 66 | ) 67 | 68 | find_library(Z3_LIBRARIES NAMES z3 libz3 69 | PATH_SUFFIXES lib bin 70 | ) 71 | 72 | # Searching for the version of the Z3 library is a best-effort task 73 | unset(Z3_VERSION_STRING) 74 | 75 | # First, try to check it dynamically, by compiling a small program that 76 | # prints Z3's version 77 | if(Z3_INCLUDE_DIR AND Z3_LIBRARIES) 78 | # We do not have the Z3 binary to query for a version. Try to use 79 | # a small C++ program to detect it via the Z3_get_version() API call. 80 | check_z3_version(${Z3_INCLUDE_DIR} ${Z3_LIBRARIES}) 81 | endif() 82 | 83 | # If the dynamic check fails, we might be cross compiling: if that's the case, 84 | # check the version in the headers, otherwise, fail with a message 85 | if(NOT Z3_VERSION_STRING AND (CMAKE_CROSSCOMPILING AND 86 | Z3_INCLUDE_DIR AND 87 | EXISTS "${Z3_INCLUDE_DIR}/z3_version.h")) 88 | # TODO: print message warning that we couldn't find a compatible lib? 89 | 90 | # Z3 4.8.1+ has the version is in a public header. 91 | file(STRINGS "${Z3_INCLUDE_DIR}/z3_version.h" 92 | z3_version_str REGEX "^#define[\t ]+Z3_MAJOR_VERSION[\t ]+.*") 93 | string(REGEX REPLACE "^.*Z3_MAJOR_VERSION[\t ]+([0-9]).*$" "\\1" 94 | Z3_MAJOR "${z3_version_str}") 95 | 96 | file(STRINGS "${Z3_INCLUDE_DIR}/z3_version.h" 97 | z3_version_str REGEX "^#define[\t ]+Z3_MINOR_VERSION[\t ]+.*") 98 | string(REGEX REPLACE "^.*Z3_MINOR_VERSION[\t ]+([0-9]).*$" "\\1" 99 | Z3_MINOR "${z3_version_str}") 100 | 101 | file(STRINGS "${Z3_INCLUDE_DIR}/z3_version.h" 102 | z3_version_str REGEX "^#define[\t ]+Z3_BUILD_NUMBER[\t ]+.*") 103 | string(REGEX REPLACE "^.*Z3_BUILD_NUMBER[\t ]+([0-9]).*$" "\\1" 104 | Z3_BUILD "${z3_version_str}") 105 | 106 | set(Z3_VERSION_STRING ${Z3_MAJOR}.${Z3_MINOR}.${Z3_BUILD}) 107 | unset(z3_version_str) 108 | endif() 109 | 110 | if(NOT Z3_VERSION_STRING) 111 | # Give up: we are unable to obtain a version of the Z3 library. Be 112 | # conservative and force the found version to 0.0.0 to make version 113 | # checks always fail. 114 | set(Z3_VERSION_STRING "0.0.0") 115 | message(WARNING "Failed to determine Z3 library version, defaulting to 0.0.0.") 116 | endif() 117 | 118 | # handle the QUIETLY and REQUIRED arguments and set Z3_FOUND to TRUE if 119 | # all listed variables are TRUE 120 | include(FindPackageHandleStandardArgs) 121 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Z3 122 | REQUIRED_VARS Z3_LIBRARIES Z3_INCLUDE_DIR 123 | VERSION_VAR Z3_VERSION_STRING) 124 | 125 | mark_as_advanced(Z3_INCLUDE_DIR Z3_LIBRARIES) 126 | -------------------------------------------------------------------------------- /cmake/modules/Findgflags.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/utils.cmake") 2 | 3 | set(RELLIC_GFLAGS_LOCATION "/usr" CACHE FILEPATH "gflags install directory") 4 | 5 | set(gflags_library_list 6 | "gflags" 7 | ) 8 | 9 | message(STATUS "Attempting to locate: gflags (hints: RELLIC_GFLAGS_LOCATION=\"${RELLIC_GFLAGS_LOCATION}\")") 10 | 11 | locateLibrary( 12 | NAME "gflags" 13 | HINT "${RELLIC_GFLAGS_LOCATION}" 14 | LIBRARIES ${gflags_library_list} 15 | MAIN_INCLUDE "gflags/gflags.h" 16 | ) 17 | -------------------------------------------------------------------------------- /cmake/modules/Findglog.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/utils.cmake") 2 | 3 | set(RELLIC_GLOG_LOCATION "/usr" CACHE FILEPATH "glog install directory") 4 | 5 | set(glog_library_list 6 | "glog" 7 | ) 8 | 9 | message(STATUS "Attempting to locate: glog (hints: RELLIC_GLOG_LOCATION=\"${RELLIC_GLOG_LOCATION}\")") 10 | 11 | locateLibrary( 12 | NAME "glog" # Compatibility name for upstream real glog import 13 | HINT "${RELLIC_GLOG_LOCATION}" 14 | LIBRARIES ${glog_library_list} 15 | MAIN_INCLUDE "glog/logging.h" 16 | ) 17 | -------------------------------------------------------------------------------- /cmake/modules/utils.cmake: -------------------------------------------------------------------------------- 1 | function(locateLibrary) 2 | cmake_parse_arguments( 3 | PARSE_ARGV 4 | 0 5 | "LOCATELIBRARY" 6 | "" 7 | "NAME;HINT" 8 | "LIBRARIES;MAIN_INCLUDE" 9 | ) 10 | 11 | add_library("${LOCATELIBRARY_NAME}" INTERFACE) 12 | 13 | # Import the (sub)libraries 14 | foreach(library ${LOCATELIBRARY_LIBRARIES}) 15 | set(target_name "${LOCATELIBRARY_NAME}_${library}") 16 | 17 | set(location_name "${target_name}_lib_location") 18 | find_library("${location_name}" 19 | NAMES "${library}" 20 | PATHS "${LOCATELIBRARY_HINT}" 21 | PATH_SUFFIXES "lib" 22 | ) 23 | 24 | if("${${location_name}}" STREQUAL "${location_name}-NOTFOUND") 25 | message(FATAL_ERROR "Failed to locate the following library: ${library}") 26 | endif() 27 | 28 | add_library("${target_name}" UNKNOWN IMPORTED GLOBAL) 29 | set_target_properties("${target_name}" PROPERTIES 30 | IMPORTED_LOCATION "${${location_name}}" 31 | ) 32 | 33 | target_link_libraries("${LOCATELIBRARY_NAME}" INTERFACE 34 | "${target_name}" 35 | ) 36 | 37 | message(STATUS "Found: ${${location_name}}") 38 | endforeach() 39 | 40 | # Locate the include header 41 | set(location_name "${target_name}_header_location") 42 | find_path("${location_name}" 43 | NAMES "${LOCATELIBRARY_MAIN_INCLUDE}" 44 | PATHS "${LOCATELIBRARY_HINT}" 45 | PATH_SUFFIXES "include" 46 | ) 47 | 48 | if("${${location_name}}" STREQUAL "${location_name}-NOTFOUND") 49 | message(FATAL_ERROR "Failed to locate the following header file: ${library}") 50 | endif() 51 | 52 | message(STATUS "Found: ${${location_name}}") 53 | 54 | target_include_directories("${LOCATELIBRARY_NAME}" INTERFACE 55 | "${${location_name}}" 56 | ) 57 | 58 | set("${LOCATELIBRARY_NAME}_FOUND" true PARENT_SCOPE) 59 | endfunction() 60 | -------------------------------------------------------------------------------- /cmake/options.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | 10 | # test options 11 | option(RELLIC_ENABLE_TESTING "Enable Test Builds" ON) 12 | option(RELLIC_ENABLE_INSTALL "Set to true to enable the install target" ON) 13 | -------------------------------------------------------------------------------- /cmake/packaging.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | # Common settings 10 | set(CPACK_PACKAGE_DESCRIPTION "Rellic") 11 | set(CPACK_PACKAGE_NAME "Rellic") 12 | set(CPACK_PACKAGE_VERSION "1.0.0") 13 | set(CPACK_PACKAGE_VENDOR "Trail of Bits") 14 | set(CPACK_PACKAGE_CONTACT "marek.surovic@trailofbits.com") 15 | set(CPACK_PACKAGE_HOMEPAGE_URL "https://www.trailofbits.com") 16 | 17 | # DEB settings 18 | set(CPACK_DEBIAN_PACKAGE_PRIORITY "extra") 19 | set(CPACK_DEBIAN_PACKAGE_SECTION "default") 20 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "${CPACK_PACKAGE_HOMEPAGE_URL}") 21 | 22 | # RPM settings 23 | set(CPACK_RPM_PACKAGE_RELEASE "${CPACK_PACKAGE_VERSION}") 24 | set(CPACK_RPM_FILE_NAME "RPM-DEFAULT") 25 | set(CPACK_RPM_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}") 26 | set(CPACK_RPM_PACKAGE_GROUP "default") 27 | set(CPACK_RPM_PACKAGE_LICENSE "Apache 2") 28 | 29 | # ZIP settings 30 | if("${CPACK_GENERATOR}" STREQUAL "ZIP") 31 | set(CPACK_SET_DESTDIR ON) 32 | endif() 33 | 34 | if (NOT CPack_CMake_INCLUDED) 35 | include("CPack") 36 | endif() 37 | -------------------------------------------------------------------------------- /cmake/settings.cmake: -------------------------------------------------------------------------------- 1 | # This is only executed once; use a macro (and not a function) so that 2 | # everything defined here does not end up in a separate namespace 3 | macro(main) 4 | # default build type 5 | if(WIN32) 6 | set(CMAKE_BUILD_TYPE Release) 7 | else() 8 | if(NOT CMAKE_BUILD_TYPE) 9 | set(CMAKE_BUILD_TYPE "RelWithDebInfo") 10 | endif() 11 | endif() 12 | 13 | # overwrite the default install prefix 14 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 15 | if(DEFINED WIN32) 16 | set(CMAKE_INSTALL_PREFIX "C:/") 17 | else() 18 | set(CMAKE_INSTALL_PREFIX "/usr/local") 19 | endif() 20 | endif() 21 | 22 | message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") 23 | 24 | # generate a compile commands JSON file. 25 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 26 | 27 | # 28 | # compiler and linker flags 29 | # 30 | 31 | # Globally set the required C++ standard 32 | 33 | set(CMAKE_CXX_EXTENSIONS OFF) 34 | 35 | if(WIN32) 36 | # warnings and compiler settings 37 | set(GLOBAL_CXXFLAGS 38 | /MD /nologo /W3 /EHsc /wd4141 /wd4146 /wd4180 /wd4244 39 | /wd4258 /wd4267 /wd4291 /wd4345 /wd4351 /wd4355 /wd4456 40 | /wd4457 /wd4458 /wd4459 /wd4503 /wd4624 /wd4722 /wd4800 41 | /wd4100 /wd4127 /wd4512 /wd4505 /wd4610 /wd4510 /wd4702 42 | /wd4245 /wd4706 /wd4310 /wd4701 /wd4703 /wd4389 /wd4611 43 | /wd4805 /wd4204 /wd4577 /wd4091 /wd4592 /wd4324 44 | ) 45 | 46 | set(GLOBAL_DEFINITIONS 47 | _CRT_SECURE_NO_DEPRECATE 48 | _CRT_SECURE_NO_WARNINGS 49 | _CRT_NONSTDC_NO_DEPRECATE 50 | _CRT_NONSTDC_NO_WARNINGS 51 | _SCL_SECURE_NO_DEPRECATE 52 | _SCL_SECURE_NO_WARNINGS 53 | GOOGLE_PROTOBUF_NO_RTTI 54 | ) 55 | 56 | else() 57 | # warnings and compiler settings 58 | set(GLOBAL_CXXFLAGS 59 | -Wall -Wextra -Wno-unused-parameter -Wno-c++98-compat 60 | -Wno-unreachable-code-return -Wno-nested-anon-types 61 | -Wno-extended-offsetof 62 | -Wno-variadic-macros -Wno-return-type-c-linkage 63 | -Wno-c99-extensions -Wno-ignored-attributes -Wno-unused-local-typedef 64 | -Wno-unknown-pragmas -Wno-unknown-warning-option -fPIC 65 | -fno-omit-frame-pointer -fvisibility-inlines-hidden 66 | -fno-asynchronous-unwind-tables 67 | ) 68 | 69 | if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") 70 | set(GLOBAL_CXXFLAGS 71 | ${GLOBAL_CXXFLAGS} 72 | -Wgnu-alignof-expression -Wno-gnu-anonymous-struct -Wno-gnu-designator 73 | -Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-statement-expression 74 | ) 75 | endif() 76 | 77 | # debug symbols 78 | if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") 79 | list(APPEND GLOBAL_CXXFLAGS 80 | -ggdb 81 | ) 82 | endif() 83 | 84 | # optimization flags and definitions 85 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 86 | list(APPEND GLOBAL_CXXFLAGS -O0) 87 | list(APPEND PROJECT_DEFINITIONS "DEBUG") 88 | else() 89 | list(APPEND GLOBAL_CXXFLAGS -O3) 90 | list(APPEND PROJECT_DEFINITIONS "NDEBUG") 91 | endif() 92 | endif() 93 | 94 | if(UNIX) 95 | if(APPLE) 96 | set(PLATFORM_NAME "macos") 97 | else() 98 | set(PLATFORM_NAME "linux") 99 | endif() 100 | 101 | elseif(WIN32) 102 | set(PLATFORM_NAME "windows") 103 | 104 | else() 105 | message("This platform is not officially supported") 106 | endif() 107 | 108 | set(SETTINGS_CMAKE_ true) 109 | endmacro() 110 | 111 | if(NOT DEFINED SETTINGS_CMAKE_) 112 | main() 113 | endif() 114 | -------------------------------------------------------------------------------- /docs/HelpingJohnny.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lifting-bits/rellic/22f65d60996958f028620e3effac4bf79a375c12/docs/HelpingJohnny.pdf -------------------------------------------------------------------------------- /docs/NoMoreGotos.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lifting-bits/rellic/22f65d60996958f028620e3effac4bf79a375c12/docs/NoMoreGotos.pdf -------------------------------------------------------------------------------- /include/rellic/AST/ASTPass.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | namespace rellic { 19 | 20 | class ASTPass { 21 | std::atomic_bool stop{false}; 22 | 23 | protected: 24 | DecompilationContext& dec_ctx; 25 | 26 | bool changed{false}; 27 | 28 | virtual void RunImpl() = 0; 29 | virtual void StopImpl() {} 30 | 31 | public: 32 | ASTPass(DecompilationContext& dec_ctx) 33 | : dec_ctx(dec_ctx) {} 34 | virtual ~ASTPass() = default; 35 | void Stop() { 36 | stop = true; 37 | StopImpl(); 38 | } 39 | 40 | bool Run() { 41 | changed = false; 42 | stop = false; 43 | RunImpl(); 44 | return changed; 45 | } 46 | 47 | unsigned Fixpoint() { 48 | unsigned iter_count{0}; 49 | changed = false; 50 | auto DoIter = [this]() { 51 | changed = false; 52 | RunImpl(); 53 | return changed; 54 | }; 55 | stop = false; 56 | while (DoIter()) { 57 | ++iter_count; 58 | } 59 | 60 | return iter_count; 61 | } 62 | 63 | bool Stopped() { return stop; } 64 | }; 65 | 66 | class CompositeASTPass : public ASTPass { 67 | std::vector> passes; 68 | 69 | protected: 70 | void StopImpl() override { 71 | for (auto& pass : passes) { 72 | pass->Stop(); 73 | } 74 | } 75 | 76 | void RunImpl() override { 77 | for (auto& pass : passes) { 78 | if (Stopped()) { 79 | break; 80 | } 81 | changed |= pass->Run(); 82 | } 83 | } 84 | 85 | public: 86 | CompositeASTPass(DecompilationContext& dec_ctx) 87 | : ASTPass(dec_ctx) {} 88 | std::vector>& GetPasses() { return passes; } 89 | }; 90 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/CXXToCDecl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "rellic/AST/ASTBuilder.h" 18 | 19 | namespace clang { 20 | class ASTUnit; 21 | } 22 | 23 | namespace rellic { 24 | 25 | class CXXToCDeclVisitor : public clang::RecursiveASTVisitor { 26 | private: 27 | clang::ASTContext &ast_ctx; 28 | clang::TranslationUnitDecl *c_tu; 29 | 30 | ASTBuilder ast; 31 | 32 | std::unordered_map c_decls; 33 | 34 | clang::QualType GetAsCType(clang::QualType type); 35 | 36 | public: 37 | CXXToCDeclVisitor(clang::ASTUnit &unit); 38 | 39 | bool shouldVisitTemplateInstantiations() { return true; } 40 | 41 | bool TraverseFunctionTemplateDecl(clang::FunctionTemplateDecl *decl) { 42 | // Ignore function templates 43 | return true; 44 | } 45 | 46 | bool TraverseClassTemplateDecl(clang::ClassTemplateDecl *decl) { 47 | // Only process class template specializations 48 | for (auto spec : decl->specializations()) { 49 | TraverseDecl(spec); 50 | } 51 | return true; 52 | } 53 | 54 | bool VisitFunctionDecl(clang::FunctionDecl *func); 55 | bool VisitCXXMethodDecl(clang::CXXMethodDecl *method); 56 | bool VisitRecordDecl(clang::RecordDecl *record); 57 | bool VisitCXXRecordDecl(clang::CXXRecordDecl *cls); 58 | bool VisitFieldDecl(clang::FieldDecl *field); 59 | }; 60 | 61 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/CondBasedRefine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "rellic/AST/ASTPass.h" 12 | #include "rellic/AST/IRToASTVisitor.h" 13 | #include "rellic/AST/TransformVisitor.h" 14 | 15 | namespace rellic { 16 | 17 | /* 18 | * This pass converts a sequence of if statements shaped like 19 | * 20 | * if(cond) { 21 | * body_then; 22 | * } 23 | * if(!cond) { 24 | * body_else; 25 | * } 26 | * 27 | * into 28 | * 29 | * if(cond) { 30 | * body_then; 31 | * } else { 32 | * body_else; 33 | * } 34 | */ 35 | class CondBasedRefine : public TransformVisitor { 36 | private: 37 | protected: 38 | void RunImpl() override; 39 | 40 | public: 41 | CondBasedRefine(DecompilationContext &dec_ctx); 42 | 43 | bool VisitCompoundStmt(clang::CompoundStmt *compound); 44 | }; 45 | 46 | } // namespace rellic 47 | -------------------------------------------------------------------------------- /include/rellic/AST/DeadStmtElim.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "rellic/AST/ASTPass.h" 12 | #include "rellic/AST/IRToASTVisitor.h" 13 | #include "rellic/AST/TransformVisitor.h" 14 | 15 | namespace rellic { 16 | 17 | /* 18 | * This pass eliminates statements that have no effect 19 | */ 20 | class DeadStmtElim : public TransformVisitor { 21 | protected: 22 | void RunImpl() override; 23 | 24 | public: 25 | DeadStmtElim(DecompilationContext &dec_ctx); 26 | 27 | bool VisitIfStmt(clang::IfStmt *ifstmt); 28 | bool VisitCompoundStmt(clang::CompoundStmt *compound); 29 | }; 30 | 31 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/DebugInfoCollector.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace rellic { 20 | 21 | using IRToNameMap = std::unordered_map; 22 | using IRToScopeMap = std::unordered_map; 23 | using IRToDITypeMap = std::unordered_map; 24 | using IRTypeToDITypeMap = std::unordered_map; 25 | using IRFuncToDITypeMap = 26 | std::unordered_map; 27 | using IRArgToDITypeMap = std::unordered_map; 28 | 29 | class DebugInfoCollector : public llvm::InstVisitor { 30 | private: 31 | IRToNameMap names; 32 | IRToScopeMap scopes; 33 | IRToDITypeMap valtypes; 34 | IRTypeToDITypeMap types; 35 | IRFuncToDITypeMap funcs; 36 | IRArgToDITypeMap args; 37 | std::unordered_set type_set; 38 | std::vector subprograms; 39 | 40 | void WalkType(llvm::Type *type, llvm::DIType *ditype); 41 | 42 | public: 43 | IRToNameMap &GetIRToNameMap() { return names; } 44 | IRToScopeMap &GetIRToScopeMap() { return scopes; } 45 | IRToDITypeMap &GetIRToDITypeMap() { return valtypes; } 46 | IRTypeToDITypeMap &GetIRTypeToDITypeMap() { return types; } 47 | IRFuncToDITypeMap &GetIRFuncToDITypeMap() { return funcs; } 48 | IRArgToDITypeMap &GetIRArgToDITypeMap() { return args; } 49 | std::unordered_set &GetTypes() { return type_set; } 50 | std::vector &GetSubprograms() { return subprograms; } 51 | 52 | void visitDbgDeclareInst(llvm::DbgDeclareInst &inst); 53 | void visitInstruction(llvm::Instruction &inst); 54 | 55 | void visitFunction(llvm::Function &func); 56 | }; 57 | 58 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/DecompilationContext.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include "rellic/AST/ASTBuilder.h" 20 | #include "rellic/AST/TypeProvider.h" 21 | 22 | namespace rellic { 23 | 24 | struct DecompilationContext { 25 | using StmtToIRMap = std::unordered_map; 26 | using ExprToUseMap = std::unordered_map; 27 | using IRToTypeDeclMap = std::unordered_map; 28 | using IRToValDeclMap = std::unordered_map; 29 | using IRToStmtMap = std::unordered_map; 30 | using ArgToTempMap = std::unordered_map; 31 | using BlockToUsesMap = 32 | std::unordered_map>; 33 | using Z3CondMap = std::unordered_map; 34 | 35 | using BBEdge = std::pair; 36 | using BrEdge = std::pair; 37 | using SwEdge = std::pair; 38 | 39 | DecompilationContext(clang::ASTUnit &ast_unit); 40 | 41 | clang::ASTUnit &ast_unit; 42 | clang::ASTContext &ast_ctx; 43 | ASTBuilder ast; 44 | 45 | std::unique_ptr type_provider; 46 | 47 | StmtToIRMap stmt_provenance; 48 | ExprToUseMap use_provenance; 49 | IRToTypeDeclMap type_decls; 50 | IRToValDeclMap value_decls; 51 | ArgToTempMap temp_decls; 52 | BlockToUsesMap outgoing_uses; 53 | z3::context z3_ctx; 54 | z3::expr_vector z3_exprs{z3_ctx}; 55 | Z3CondMap conds; 56 | 57 | clang::Expr *marker_expr; 58 | 59 | std::unordered_map z3_br_edges_inv; 60 | 61 | // Pairs do not have a std::hash specialization so we can't use unordered maps 62 | // here. If this turns out to be a performance issue, investigate adding hash 63 | // specializations for these specifically 64 | std::map z3_br_edges; 65 | 66 | std::unordered_map z3_sw_vars; 67 | std::unordered_map z3_sw_vars_inv; 68 | std::map z3_sw_edges; 69 | 70 | std::map z3_edges; 71 | std::unordered_map reaching_conds; 72 | 73 | size_t num_literal_structs = 0; 74 | size_t num_declared_structs = 0; 75 | 76 | // Inserts an expression into z3_exprs and returns its index 77 | unsigned InsertZExpr(const z3::expr &e); 78 | 79 | clang::QualType GetQualType(llvm::Type *type); 80 | }; 81 | 82 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/ExprCombine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "rellic/AST/TransformVisitor.h" 12 | 13 | namespace rellic { 14 | 15 | /* 16 | * This pass performs a number of different trasnformations on expressions, 17 | * like turning *&a into a, or !(a == b) into a != b 18 | */ 19 | class ExprCombine : public TransformVisitor { 20 | protected: 21 | void RunImpl() override; 22 | 23 | public: 24 | ExprCombine(DecompilationContext &dec_ctx); 25 | 26 | bool VisitCStyleCastExpr(clang::CStyleCastExpr *cast); 27 | bool VisitUnaryOperator(clang::UnaryOperator *op); 28 | bool VisitBinaryOperator(clang::BinaryOperator *op); 29 | bool VisitArraySubscriptExpr(clang::ArraySubscriptExpr *expr); 30 | bool VisitMemberExpr(clang::MemberExpr *expr); 31 | bool VisitParenExpr(clang::ParenExpr *paren); 32 | }; 33 | 34 | } // namespace rellic 35 | -------------------------------------------------------------------------------- /include/rellic/AST/GenerateAST.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "rellic/AST/ASTBuilder.h" 21 | #include "rellic/AST/IRToASTVisitor.h" 22 | 23 | namespace rellic { 24 | 25 | class GenerateAST : public llvm::AnalysisInfoMixin { 26 | private: 27 | friend llvm::AnalysisInfoMixin; 28 | static llvm::AnalysisKey Key; 29 | 30 | constexpr static unsigned poison_idx = std::numeric_limits::max(); 31 | z3::expr ToExpr(unsigned idx); 32 | 33 | rellic::IRToASTVisitor ast_gen; 34 | DecompilationContext &dec_ctx; 35 | ASTBuilder * 36 | bool reaching_conds_changed{true}; 37 | std::unordered_map block_stmts; 38 | std::unordered_map region_stmts; 39 | 40 | llvm::DominatorTree *domtree; 41 | llvm::RegionInfo *regions; 42 | llvm::LoopInfo *loops; 43 | 44 | std::vector rpo_walk; 45 | 46 | // GetOrCreateEdgeForBranch(branch, true) will return the index of an 47 | // expression that is true when branch is taken. 48 | // Viceversa, GetOrCreateEdgeForBranch(branch, false) is an expression that 49 | // will be true when branch is not taken 50 | unsigned GetOrCreateEdgeForBranch(llvm::BranchInst *inst, bool cond); 51 | 52 | // Returns the index of an expression containing a numerical variable that 53 | // represents the condition of a switch. 54 | unsigned GetOrCreateVarForSwitch(llvm::SwitchInst *inst); 55 | // Returns the index of an expression that is true when a particular case of a 56 | // switch is taken. If c is nullptr, the expression for the default case will 57 | // be returned. 58 | unsigned GetOrCreateEdgeForSwitch(llvm::SwitchInst *inst, 59 | llvm::ConstantInt *c); 60 | 61 | unsigned GetOrCreateEdgeCond(llvm::BasicBlock *from, llvm::BasicBlock *to); 62 | unsigned GetReachingCond(llvm::BasicBlock *block); 63 | void CreateReachingCond(llvm::BasicBlock *block); 64 | 65 | std::vector CreateBasicBlockStmts(llvm::BasicBlock *block); 66 | std::vector CreateRegionStmts(llvm::Region *region); 67 | 68 | using BBSet = std::unordered_set; 69 | 70 | void RefineLoopSuccessors(llvm::Loop *loop, BBSet &members, 71 | BBSet &successors); 72 | 73 | clang::CompoundStmt *StructureAcyclicRegion(llvm::Region *region); 74 | clang::CompoundStmt *StructureCyclicRegion(llvm::Region *region); 75 | clang::CompoundStmt *StructureSwitchRegion(llvm::Region *region); 76 | clang::CompoundStmt *StructureRegion(llvm::Region *region); 77 | 78 | public: 79 | using Result = llvm::PreservedAnalyses; 80 | GenerateAST(DecompilationContext &dec_ctx); 81 | 82 | Result run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM); 83 | Result run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM); 84 | 85 | static void run(llvm::Module &M, DecompilationContext &dec_ctx); 86 | }; 87 | 88 | } // namespace rellic 89 | -------------------------------------------------------------------------------- /include/rellic/AST/IRToASTVisitor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #include "rellic/AST/ASTBuilder.h" 26 | 27 | namespace rellic { 28 | class IRToASTVisitor { 29 | private: 30 | DecompilationContext &dec_ctx; 31 | ASTBuilder * 32 | 33 | void VisitArgument(llvm::Argument &arg); 34 | 35 | public: 36 | IRToASTVisitor(DecompilationContext &dec_ctx); 37 | 38 | clang::Expr *CreateOperandExpr(llvm::Use &val); 39 | clang::Expr *CreateConstantExpr(llvm::Constant *constant); 40 | clang::Expr *ConvertExpr(z3::expr expr); 41 | 42 | void VisitGlobalVar(llvm::GlobalVariable &var); 43 | void VisitFunctionDecl(llvm::Function &func); 44 | void VisitBasicBlock(llvm::BasicBlock &block, 45 | std::vector &stmts); 46 | }; 47 | 48 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/InferenceRule.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | #include "rellic/AST/IRToASTVisitor.h" 15 | 16 | namespace clang { 17 | class ASTUnit; 18 | } 19 | 20 | namespace rellic { 21 | 22 | class InferenceRule : public clang::ast_matchers::MatchFinder::MatchCallback { 23 | protected: 24 | clang::ast_matchers::StatementMatcher cond; 25 | const clang::Stmt *match; 26 | 27 | public: 28 | InferenceRule(clang::ast_matchers::StatementMatcher matcher) 29 | : cond(matcher), match(nullptr) {} 30 | 31 | operator bool() { return match; } 32 | 33 | const clang::ast_matchers::StatementMatcher &GetCondition() const { 34 | return cond; 35 | } 36 | 37 | virtual clang::Stmt *GetOrCreateSubstitution(DecompilationContext &dec_ctx, 38 | clang::Stmt *stmt) = 0; 39 | }; 40 | 41 | clang::Stmt *ApplyFirstMatchingRule( 42 | DecompilationContext &dec_ctx, clang::Stmt *stmt, 43 | std::vector> &rules); 44 | 45 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/LocalDeclRenamer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | #include "rellic/AST/DebugInfoCollector.h" 15 | #include "rellic/AST/IRToASTVisitor.h" 16 | #include "rellic/AST/TransformVisitor.h" 17 | 18 | namespace rellic { 19 | 20 | using ValDeclToIRMap = std::unordered_map; 21 | 22 | class LocalDeclRenamer : public TransformVisitor { 23 | private: 24 | ValDeclToIRMap decls; 25 | 26 | // Stores currently visible names, with scope awareness 27 | std::vector> seen_names; 28 | 29 | std::unordered_set renamed_decls; 30 | IRToNameMap &names; 31 | 32 | bool IsNameVisible(const std::string &name); 33 | 34 | protected: 35 | void RunImpl() override; 36 | 37 | public: 38 | LocalDeclRenamer(DecompilationContext &dec_ctx, IRToNameMap &names); 39 | 40 | bool shouldTraversePostOrder() override; 41 | bool VisitVarDecl(clang::VarDecl *decl); 42 | bool TraverseFunctionDecl(clang::FunctionDecl *decl); 43 | }; 44 | 45 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/LoopRefine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "rellic/AST/TransformVisitor.h" 12 | 13 | namespace rellic { 14 | 15 | /* 16 | * This pass transforms unbounded loops with a break in their body into loops 17 | * with a condition. For example, 18 | * 19 | * while(1U) { 20 | * if(cond) { 21 | * break; 22 | * } 23 | * body; 24 | * } 25 | * 26 | * becomes 27 | * 28 | * while(!cond) { 29 | * body; 30 | * } 31 | */ 32 | class LoopRefine : public TransformVisitor { 33 | protected: 34 | void RunImpl() override; 35 | 36 | public: 37 | LoopRefine(DecompilationContext &dec_ctx); 38 | 39 | bool VisitWhileStmt(clang::WhileStmt *loop); 40 | }; 41 | 42 | } // namespace rellic 43 | -------------------------------------------------------------------------------- /include/rellic/AST/MaterializeConds.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #include "rellic/AST/IRToASTVisitor.h" 14 | #include "rellic/AST/TransformVisitor.h" 15 | 16 | namespace rellic { 17 | 18 | /* 19 | * This pass substitutes the marker expression in loops and `if` statements for 20 | * their translation from Z3 formulas 21 | */ 22 | class MaterializeConds : public TransformVisitor { 23 | private: 24 | IRToASTVisitor ast_gen; 25 | 26 | protected: 27 | void RunImpl() override; 28 | 29 | public: 30 | MaterializeConds(DecompilationContext &dec_ctx); 31 | 32 | bool VisitIfStmt(clang::IfStmt *stmt); 33 | bool VisitWhileStmt(clang::WhileStmt *loop); 34 | bool VisitDoStmt(clang::DoStmt *loop); 35 | }; 36 | 37 | } // namespace rellic 38 | -------------------------------------------------------------------------------- /include/rellic/AST/NestedCondProp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "rellic/AST/ASTPass.h" 12 | 13 | namespace rellic { 14 | 15 | /* 16 | * This pass propagates the condition of a while or do-while statement to all 17 | * subsequent if, while and do-while statements in the same lexical scope. 18 | * 19 | * while(a) { 20 | * foo(); 21 | * } 22 | * if(a) { 23 | * bar(); 24 | * } 25 | * 26 | * turns into 27 | * 28 | * while(a) { 29 | * foo(); 30 | * } 31 | * if(1U) { 32 | * bar(); 33 | * } 34 | * 35 | * It also propagates the conditions of if and while statements to their nested 36 | * statements. 37 | * 38 | * if(a) { 39 | * if(a && b) { 40 | * foo(); 41 | * } 42 | * } 43 | * 44 | * turns into 45 | * 46 | * if(a) { 47 | * if(1U && b) { 48 | * foo(); 49 | * } 50 | * } 51 | */ 52 | class NestedCondProp : public ASTPass { 53 | protected: 54 | void RunImpl() override; 55 | 56 | public: 57 | NestedCondProp(DecompilationContext& dec_ctx); 58 | }; 59 | 60 | } // namespace rellic 61 | -------------------------------------------------------------------------------- /include/rellic/AST/NestedScopeCombine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "rellic/AST/TransformVisitor.h" 12 | 13 | namespace rellic { 14 | 15 | /* 16 | * This pass combines the bodies of trivially true if statements and compound 17 | * statements into the body of their parent's body, and deletes trivially false 18 | * if statements. For example, 19 | * 20 | * { 21 | * if(1U) { 22 | * body1; 23 | * } 24 | * if(0U) { 25 | * body2; 26 | * } 27 | * } 28 | * 29 | * becomes 30 | * 31 | * body1; 32 | */ 33 | class NestedScopeCombine : public TransformVisitor { 34 | protected: 35 | void RunImpl() override; 36 | 37 | public: 38 | NestedScopeCombine(DecompilationContext &dec_ctx); 39 | 40 | bool VisitIfStmt(clang::IfStmt *ifstmt); 41 | bool VisitWhileStmt(clang::WhileStmt *stmt); 42 | bool VisitCompoundStmt(clang::CompoundStmt *compound); 43 | }; 44 | 45 | } // namespace rellic 46 | -------------------------------------------------------------------------------- /include/rellic/AST/ReachBasedRefine.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "rellic/AST/TransformVisitor.h" 12 | 13 | namespace rellic { 14 | 15 | /* 16 | * This pass restructures a sequence of if statements that have a shape like 17 | * 18 | * if(cond1 && !cond2 && !cond3) { 19 | * body1; 20 | * } 21 | * if(cond2 && !cond1 && !cond3) { 22 | * body2; 23 | * } 24 | * if(cond3 && !cond1 && !cond2) { 25 | * body3; 26 | * } 27 | * 28 | * into 29 | * 30 | * if(cond1) { 31 | * body1; 32 | * } else if(cond2) { 33 | * body2; 34 | * } else if(cond3) { 35 | * body3; 36 | * } 37 | */ 38 | class ReachBasedRefine : public TransformVisitor { 39 | private: 40 | protected: 41 | void RunImpl() override; 42 | 43 | public: 44 | ReachBasedRefine(DecompilationContext &dec_ctx); 45 | 46 | bool VisitCompoundStmt(clang::CompoundStmt *compound); 47 | }; 48 | 49 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/StructFieldRenamer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #include "rellic/AST/DebugInfoCollector.h" 14 | #include "rellic/AST/IRToASTVisitor.h" 15 | #include "rellic/AST/TransformVisitor.h" 16 | 17 | namespace rellic { 18 | 19 | using TypeDeclToIRMap = std::unordered_map; 20 | 21 | class StructFieldRenamer 22 | : public ASTPass, 23 | public clang::RecursiveASTVisitor { 24 | private: 25 | TypeDeclToIRMap decls; 26 | IRTypeToDITypeMap &types; 27 | 28 | protected: 29 | void RunImpl() override; 30 | 31 | public: 32 | StructFieldRenamer(DecompilationContext &dec_ctx, IRTypeToDITypeMap &types); 33 | 34 | bool VisitRecordDecl(clang::RecordDecl *decl); 35 | }; 36 | 37 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/StructGenerator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "rellic/AST/ASTBuilder.h" 20 | 21 | namespace rellic { 22 | struct OffsetDIDerivedType; 23 | 24 | class StructGenerator { 25 | clang::ASTContext& ast_ctx; 26 | rellic::ASTBuilder ast; 27 | std::unordered_map 28 | fwd_decl_records{}; 29 | std::unordered_map enum_types{}; 30 | std::unordered_map 31 | typedef_decls{}; 32 | std::unordered_set visible_structs; 33 | std::unordered_set visible_unions; 34 | std::unordered_set visible_enums; 35 | std::unordered_set visible_tdefs; 36 | std::unordered_set visible_values; 37 | 38 | using DeclToDbgInfo = 39 | std::unordered_map; 40 | void VisitFields(clang::RecordDecl* decl, llvm::DICompositeType* s, 41 | DeclToDbgInfo& map, bool isUnion); 42 | 43 | std::string GetUniqueName(const std::string& name, 44 | std::unordered_set& names); 45 | clang::RecordDecl* GetRecordDecl(llvm::DICompositeType* t); 46 | clang::QualType GetEnumDecl(llvm::DICompositeType* t); 47 | 48 | void DefineNonPackedStruct(clang::RecordDecl* decl, 49 | std::vector& fields); 50 | uint64_t GetLayoutSize(const clang::ASTRecordLayout& layout); 51 | 52 | clang::QualType BuildArray(llvm::DICompositeType* a); 53 | clang::QualType BuildBasic(llvm::DIBasicType* b, int sizeHint); 54 | clang::QualType BuildSubroutine(llvm::DISubroutineType* s); 55 | clang::QualType BuildComposite(llvm::DICompositeType* type); 56 | clang::QualType BuildDerived(llvm::DIDerivedType* d, int sizeHint); 57 | clang::QualType BuildType(llvm::DIType* t, int sizeHint = -1); 58 | 59 | void DefineComposite(llvm::DICompositeType* s); 60 | void DefineStruct(llvm::DICompositeType* s); 61 | void DefineUnion(llvm::DICompositeType* s); 62 | 63 | void VisitType(llvm::DIType* t, std::vector& list, 64 | std::unordered_set& visited); 65 | 66 | public: 67 | StructGenerator(clang::ASTUnit& ast_unit); 68 | 69 | clang::QualType GetType(llvm::DIType* t); 70 | 71 | template 72 | void GenerateDecls(It begin, It end) { 73 | std::unordered_set visible_types; 74 | std::vector sorted_types{}; 75 | std::unordered_set visited_types{}; 76 | for (auto i{begin}; i != end; ++i) { 77 | VisitType(*i, sorted_types, visited_types); 78 | } 79 | 80 | for (auto type : sorted_types) { 81 | DefineComposite(type); 82 | } 83 | } 84 | 85 | std::vector GetAccessor(clang::Expr* base, 86 | clang::RecordDecl* decl, 87 | unsigned offset, unsigned length); 88 | }; 89 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/SubprogramGenerator.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "rellic/AST/ASTBuilder.h" 18 | #include "rellic/AST/StructGenerator.h" 19 | 20 | namespace rellic { 21 | 22 | class SubprogramGenerator { 23 | clang::ASTContext& ast_ctx; 24 | StructGenerator& struct_gen; 25 | rellic::ASTBuilder ast; 26 | 27 | public: 28 | SubprogramGenerator(clang::ASTUnit& ast_unit, StructGenerator& struct_gen); 29 | clang::FunctionDecl* VisitSubprogram(llvm::DISubprogram* subp); 30 | }; 31 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/TransformVisitor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #include 14 | 15 | #include "rellic/AST/ASTPass.h" 16 | #include "rellic/AST/Util.h" 17 | 18 | namespace rellic { 19 | 20 | using StmtSubMap = std::unordered_map; 21 | 22 | template 23 | class TransformVisitor : public ASTPass, 24 | public clang::RecursiveASTVisitor { 25 | protected: 26 | StmtSubMap substitutions; 27 | 28 | void CopyProvenance(clang::Stmt *from, clang::Stmt *to) { 29 | ::rellic::CopyProvenance(from, to, dec_ctx.stmt_provenance); 30 | } 31 | 32 | void CopyProvenance(clang::Expr *from, clang::Expr *to) { 33 | ::rellic::CopyProvenance(from, to, dec_ctx.use_provenance); 34 | } 35 | 36 | bool ReplaceChildren(clang::Stmt *stmt, StmtSubMap &repl_map) { 37 | auto change = false; 38 | for (auto c_it = stmt->child_begin(); c_it != stmt->child_end(); ++c_it) { 39 | auto s_it = repl_map.find(*c_it); 40 | if (s_it != repl_map.end()) { 41 | *c_it = s_it->second; 42 | CopyProvenance(s_it->first, s_it->second); 43 | if (clang::isa(s_it->first) && 44 | clang::isa(s_it->second)) { 45 | auto from_expr{clang::cast(s_it->first)}; 46 | auto to_expr{clang::cast(s_it->second)}; 47 | CopyProvenance(from_expr, to_expr); 48 | } 49 | change = true; 50 | } 51 | } 52 | return change; 53 | } 54 | 55 | void RunImpl() override { substitutions.clear(); } 56 | 57 | public: 58 | TransformVisitor(DecompilationContext &dec_ctx) : ASTPass(dec_ctx) {} 59 | 60 | virtual bool shouldTraversePostOrder() { return true; } 61 | 62 | bool VisitFunctionDecl(clang::FunctionDecl *fdecl) { 63 | // DLOG(INFO) << "VisitFunctionDecl"; 64 | if (auto body = fdecl->getBody()) { 65 | auto iter = substitutions.find(body); 66 | if (iter != substitutions.end()) { 67 | fdecl->setBody(iter->second); 68 | changed = true; 69 | } 70 | } 71 | return !Stopped(); 72 | } 73 | 74 | bool VisitStmt(clang::Stmt *stmt) { 75 | // DLOG(INFO) << "VisitStmt"; 76 | changed |= ReplaceChildren(stmt, substitutions); 77 | return !Stopped(); 78 | } 79 | }; 80 | 81 | } // namespace rellic 82 | -------------------------------------------------------------------------------- /include/rellic/AST/TypeProvider.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | #include 11 | #include 12 | #include 13 | 14 | #include "rellic/AST/ASTBuilder.h" 15 | 16 | namespace rellic { 17 | struct DecompilationContext; 18 | 19 | class TypeProvider { 20 | protected: 21 | DecompilationContext& dec_ctx; 22 | 23 | public: 24 | TypeProvider(DecompilationContext& dec_ctx); 25 | virtual ~TypeProvider(); 26 | 27 | // Returns the return type of a function if available. 28 | // A null return value is assumed to mean that no info is available. 29 | virtual clang::QualType GetFunctionReturnType(llvm::Function& func); 30 | 31 | // Returns the type of the argument if available. 32 | // A null return value is assumed to mean that no info is available. 33 | virtual clang::QualType GetArgumentType(llvm::Argument& arg); 34 | 35 | // Returns the type of a global variable if available. 36 | // A null return value is assumed to mean that no info is available. 37 | virtual clang::QualType GetGlobalVarType(llvm::GlobalVariable& gvar); 38 | }; 39 | 40 | class TypeProviderCombiner : public TypeProvider { 41 | private: 42 | std::vector> providers; 43 | 44 | public: 45 | TypeProviderCombiner(DecompilationContext& dec_ctx); 46 | template 47 | void AddProvider(TArgs&&... args) { 48 | providers.push_back( 49 | std::make_unique(dec_ctx, std::forward(args)...)); 50 | } 51 | 52 | void AddProvider(std::unique_ptr provider); 53 | 54 | clang::QualType GetFunctionReturnType(llvm::Function& func) override; 55 | clang::QualType GetArgumentType(llvm::Argument& arg) override; 56 | clang::QualType GetGlobalVarType(llvm::GlobalVariable& gvar) override; 57 | }; 58 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/Util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "rellic/AST/DecompilationContext.h" 12 | 13 | namespace rellic { 14 | 15 | unsigned GetHash(clang::ASTContext &ctx, clang::Stmt *stmt); 16 | bool IsEquivalent(clang::Expr *a, clang::Expr *b); 17 | template 18 | bool Replace(TFrom *from, clang::Expr *to, TIn **in) { 19 | auto from_expr{clang::cast(from)}; 20 | auto in_expr{clang::cast(*in)}; 21 | if (IsEquivalent(in_expr, from_expr)) { 22 | *in = to; 23 | return true; 24 | } else { 25 | bool changed{false}; 26 | for (auto child{(*in)->child_begin()}; child != (*in)->child_end(); 27 | ++child) { 28 | changed |= Replace(from_expr, to, &*child); 29 | } 30 | return changed; 31 | } 32 | } 33 | 34 | template 35 | size_t GetNumDecls(clang::DeclContext *decl_ctx) { 36 | size_t result = 0; 37 | for (auto decl : decl_ctx->decls()) { 38 | if (clang::isa(decl)) { 39 | ++result; 40 | } 41 | } 42 | return result; 43 | } 44 | 45 | template 46 | void CopyProvenance(TKey1 *from, TKey2 *to, 47 | std::unordered_map &map) { 48 | map[to] = map[from]; 49 | } 50 | 51 | clang::Expr *Clone(clang::ASTUnit &unit, clang::Expr *stmt, 52 | DecompilationContext::ExprToUseMap &provenance); 53 | 54 | std::string ClangThingToString(const clang::Stmt *stmt); 55 | std::string ClangThingToString(clang::QualType ty); 56 | 57 | z3::goal ApplyTactic(const z3::tactic &tactic, z3::expr expr); 58 | 59 | bool Prove(z3::expr expr); 60 | 61 | z3::expr HeavySimplify(z3::expr expr); 62 | z3::expr_vector Clone(z3::expr_vector &vec); 63 | 64 | // Tries to keep each subformula sorted by its id so that they don't get 65 | // shuffled around by simplification 66 | z3::expr OrderById(z3::expr expr); 67 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/AST/Z3CondSimplify.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #include "rellic/AST/ASTPass.h" 14 | 15 | namespace rellic { 16 | 17 | /* 18 | * This pass simplifies conditions using Z3 by trying to remove terms that are 19 | * trivially true or false 20 | */ 21 | class Z3CondSimplify : public ASTPass { 22 | private: 23 | protected: 24 | void RunImpl() override; 25 | 26 | public: 27 | Z3CondSimplify(DecompilationContext& dec_ctx); 28 | }; 29 | 30 | } // namespace rellic 31 | -------------------------------------------------------------------------------- /include/rellic/BC/Util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #include 14 | 15 | namespace llvm { 16 | class Module; 17 | class Type; 18 | class Value; 19 | class LLVMContext; 20 | class GlobalObject; 21 | class DIType; 22 | } // namespace llvm 23 | 24 | namespace rellic { 25 | // Serialize an LLVM object into a string. 26 | std::string LLVMThingToString(llvm::Value *thing); 27 | std::string LLVMThingToString(llvm::Type *thing); 28 | std::string LLVMThingToString(llvm::DIType *thing); 29 | 30 | // Try to verify a module. 31 | bool VerifyModule(llvm::Module *module); 32 | 33 | // Parses and loads a bitcode file into memory. 34 | llvm::Module *LoadModuleFromFile(llvm::LLVMContext *context, 35 | std::string file_name, 36 | bool allow_failure = false); 37 | llvm::Module *LoadModuleFromMemory(llvm::LLVMContext *context, 38 | std::string file_data, 39 | bool allow_failure = false); 40 | 41 | // Check if an intrinsic ID is an annotation 42 | bool IsAnnotationIntrinsic(llvm::Intrinsic::ID id); 43 | 44 | // check if a global object is llvm metadata 45 | bool IsGlobalMetadata(const llvm::GlobalObject &go); 46 | 47 | void CloneMetadataInto( 48 | llvm::Instruction *dst, 49 | const llvm::SmallVector, 16u> &mds); 50 | 51 | void CopyMetadataTo(llvm::Value *src, llvm::Value *dst); 52 | 53 | void RemovePHINodes(llvm::Module &module); 54 | 55 | void LowerSwitches(llvm::Module &module); 56 | 57 | // ARM backends (and probably some others) have a tendency to generate functions 58 | // that make use of arrays passed by value, e.g. by value array arguments and 59 | // using `insertvalue` to create copies with different elements. These 60 | // operations do not have a clear equivalent in C, and are better handled by 61 | // patching the bitcode to work around their semantics. 62 | 63 | // Converts `insertvalue` instructions into an equivalent `alloca`, `load` and 64 | // `store` sequences. 65 | void RemoveInsertValues(llvm::Module &module); 66 | 67 | // Converts by value array arguments and wraps them into a struct, so that 68 | // semantics are preserved in C 69 | void ConvertArrayArguments(llvm::Module &module); 70 | } // namespace rellic -------------------------------------------------------------------------------- /include/rellic/BC/Version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | #define LLVM_VERSION(major, minor) ((major * 100) + minor) 14 | 15 | #define LLVM_VERSION_NUMBER LLVM_VERSION(LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR) 16 | 17 | #if LLVM_VERSION_NUMBER < LLVM_VERSION(3, 5) 18 | #error "Minimum supported LLVM version is 3.5" 19 | #endif 20 | 21 | #if LLVM_VERSION_NUMBER < LLVM_VERSION(5, 0) 22 | #define IF_LLVM_LT_50(...) __VA_ARGS__ 23 | #define IF_LLVM_LT_50_(...) __VA_ARGS__, 24 | #define _IF_LLVM_LT_50(...) , __VA_ARGS__ 25 | #define IF_LLVM_GTE_50(...) 26 | #define IF_LLVM_GTE_50_(...) 27 | #define _IF_LLVM_GTE_50(...) 28 | #else 29 | #define IF_LLVM_LT_50(...) 30 | #define IF_LLVM_LT_50_(...) 31 | #define _IF_LLVM_LT_50(...) 32 | #define IF_LLVM_GTE_50(...) __VA_ARGS__ 33 | #define IF_LLVM_GTE_50_(...) __VA_ARGS__, 34 | #define _IF_LLVM_GTE_50(...) , __VA_ARGS__ 35 | #endif 36 | 37 | #if LLVM_VERSION_NUMBER < LLVM_VERSION(4, 0) 38 | #define IF_LLVM_LT_40(...) __VA_ARGS__ 39 | #define IF_LLVM_LT_40_(...) __VA_ARGS__, 40 | #define _IF_LLVM_LT_40(...) , __VA_ARGS__ 41 | #define IF_LLVM_GTE_40(...) 42 | #define IF_LLVM_GTE_40_(...) 43 | #define _IF_LLVM_GTE_40(...) 44 | #else 45 | #define IF_LLVM_LT_40(...) 46 | #define IF_LLVM_LT_40_(...) 47 | #define _IF_LLVM_LT_40(...) 48 | #define IF_LLVM_GTE_40(...) __VA_ARGS__ 49 | #define IF_LLVM_GTE_40_(...) __VA_ARGS__, 50 | #define _IF_LLVM_GTE_40(...) , __VA_ARGS__ 51 | #endif 52 | 53 | #if LLVM_VERSION_NUMBER < LLVM_VERSION(3, 9) 54 | #define IF_LLVM_LT_39(...) __VA_ARGS__ 55 | #define IF_LLVM_LT_39_(...) __VA_ARGS__, 56 | #define _IF_LLVM_LT_39(...) , __VA_ARGS__ 57 | #define IF_LLVM_GTE_39(...) 58 | #define IF_LLVM_GTE_39_(...) 59 | #define _IF_LLVM_GTE_39(...) 60 | #else 61 | #define IF_LLVM_LT_39(...) 62 | #define IF_LLVM_LT_39_(...) 63 | #define _IF_LLVM_LT_39(...) 64 | #define IF_LLVM_GTE_39(...) __VA_ARGS__ 65 | #define IF_LLVM_GTE_39_(...) __VA_ARGS__, 66 | #define _IF_LLVM_GTE_39(...) , __VA_ARGS__ 67 | #endif 68 | 69 | #if LLVM_VERSION_NUMBER < LLVM_VERSION(3, 8) 70 | #define IF_LLVM_LT_38(...) __VA_ARGS__ 71 | #define IF_LLVM_LT_38_(...) __VA_ARGS__, 72 | #define _IF_LLVM_LT_38(...) , __VA_ARGS__ 73 | #define IF_LLVM_GTE_38(...) 74 | #define IF_LLVM_GTE_38_(...) 75 | #define _IF_LLVM_GTE_38(...) 76 | #else 77 | #define IF_LLVM_LT_38(...) 78 | #define IF_LLVM_LT_38_(...) 79 | #define _IF_LLVM_LT_38(...) 80 | #define IF_LLVM_GTE_38(...) __VA_ARGS__ 81 | #define IF_LLVM_GTE_38_(...) __VA_ARGS__, 82 | #define _IF_LLVM_GTE_38(...) , __VA_ARGS__ 83 | #endif 84 | 85 | #if LLVM_VERSION_NUMBER < LLVM_VERSION(3, 7) 86 | #define IF_LLVM_LT_37(...) __VA_ARGS__ 87 | #define IF_LLVM_LT_37_(...) __VA_ARGS__, 88 | #define _IF_LLVM_LT_37(...) , __VA_ARGS__ 89 | #define IF_LLVM_GTE_37(...) 90 | #define IF_LLVM_GTE_37_(...) 91 | #define _IF_LLVM_GTE_37(...) 92 | #else 93 | #define IF_LLVM_LT_37(...) 94 | #define IF_LLVM_LT_37_(...) 95 | #define _IF_LLVM_LT_37(...) 96 | #define IF_LLVM_GTE_37(...) __VA_ARGS__ 97 | #define IF_LLVM_GTE_37_(...) __VA_ARGS__, 98 | #define _IF_LLVM_GTE_37(...) , __VA_ARGS__ 99 | #endif 100 | 101 | #if LLVM_VERSION_NUMBER < LLVM_VERSION(3, 6) 102 | #define IF_LLVM_LT_36(...) __VA_ARGS__ 103 | #define IF_LLVM_LT_36_(...) __VA_ARGS__, 104 | #define _IF_LLVM_LT_36(...) , __VA_ARGS__ 105 | #define IF_LLVM_GTE_36(...) 106 | #define IF_LLVM_GTE_36_(...) 107 | #define _IF_LLVM_GTE_36(...) 108 | #else 109 | #define IF_LLVM_LT_36(...) 110 | #define IF_LLVM_LT_36_(...) 111 | #define _IF_LLVM_LT_36(...) 112 | #define IF_LLVM_GTE_36(...) __VA_ARGS__ 113 | #define IF_LLVM_GTE_36_(...) __VA_ARGS__, 114 | #define _IF_LLVM_GTE_36(...) , __VA_ARGS__ 115 | #endif 116 | 117 | #define IF_LLVM_LT(major, minor, ...) IF_LLVM_LT_##major##minor(__VA_ARGS__) 118 | 119 | #define IF_LLVM_LT_(major, minor, ...) IF_LLVM_LT_##major##minor##_(__VA_ARGS__) 120 | 121 | #define _IF_LLVM_LT(major, minor, ...) _IF_LLVM_LT_##major##minor(__VA_ARGS__) 122 | 123 | #define IF_LLVM_GTE(major, minor, ...) IF_LLVM_GTE_##major##minor(__VA_ARGS__) 124 | 125 | #define IF_LLVM_GTE_(major, minor, ...) \ 126 | IF_LLVM_GTE_##major##minor##_(__VA_ARGS__) 127 | 128 | #define _IF_LLVM_GTE(major, minor, ...) _IF_LLVM_GTE_##major##minor(__VA_ARGS__) 129 | -------------------------------------------------------------------------------- /include/rellic/Dec2Hex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | namespace rellic { 19 | // Converts integer literals in `ast_ctx` to hexadecimal form when 20 | // `shouldConvert` return true. 21 | // 22 | // Note: the context should have valid source range information, i.e. cannot be 23 | // the one directly generated by Rellic. To obtain a context with valid source 24 | // range information, serialize the AST to a string and parse it back into a 25 | // context. 26 | void ConvertIntegerLiteralsToHex( 27 | clang::ASTContext& ast_ctx, llvm::raw_ostream& os, 28 | std::function shouldConvert); 29 | } // namespace rellic 30 | -------------------------------------------------------------------------------- /include/rellic/Decompiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "Result.h" 19 | #include "rellic/AST/TypeProvider.h" 20 | 21 | namespace rellic { 22 | 23 | /* This additional level of indirection is needed to alleviate the users from 24 | * the burden of having to instantiate custom TypeProviders before the actual 25 | * DecompilationContext has been created */ 26 | class TypeProviderFactory { 27 | public: 28 | virtual ~TypeProviderFactory() = default; 29 | virtual std::unique_ptr create(DecompilationContext& ctx) = 0; 30 | }; 31 | 32 | template 33 | class SimpleTypeProviderFactory final : public TypeProviderFactory { 34 | public: 35 | std::unique_ptr create(DecompilationContext& ctx) override { 36 | return std::make_unique(ctx); 37 | } 38 | }; 39 | 40 | struct DecompilationOptions { 41 | using TypeProviderFactoryPtr = std::unique_ptr; 42 | 43 | bool lower_switches = false; 44 | bool remove_phi_nodes = false; 45 | 46 | // Additional type providers to be used during code generation. 47 | // Providers added later will have higher priority. 48 | std::vector additional_providers; 49 | }; 50 | 51 | struct DecompilationResult { 52 | using StmtToIRMap = 53 | std::unordered_map; 54 | using DeclToIRMap = 55 | std::unordered_map; 56 | using TypeDeclToIRMap = 57 | std::unordered_map; 58 | using ExprToUseMap = std::unordered_map; 59 | using IRToStmtMap = 60 | std::unordered_map; 61 | using IRToDeclMap = 62 | std::unordered_map; 63 | using IRToTypeDeclMap = 64 | std::unordered_map; 65 | using UseToExprMap = std::unordered_map; 66 | 67 | std::unique_ptr module; 68 | std::unique_ptr ast; 69 | StmtToIRMap stmt_provenance_map; 70 | IRToStmtMap value_to_stmt_map; 71 | DeclToIRMap decl_provenance_map; 72 | IRToDeclMap value_to_decl_map; 73 | TypeDeclToIRMap type_provenance_map; 74 | IRToTypeDeclMap type_to_decl_map; 75 | ExprToUseMap expr_use_map; 76 | UseToExprMap use_expr_map; 77 | }; 78 | 79 | struct DecompilationError { 80 | std::unique_ptr module; 81 | std::unique_ptr ast; 82 | std::string message; 83 | }; 84 | 85 | Result Decompile( 86 | std::unique_ptr module, DecompilationOptions options = {}); 87 | } // namespace rellic 88 | -------------------------------------------------------------------------------- /include/rellic/Exception.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace rellic { 16 | class Exception : public std::runtime_error { 17 | public: 18 | Exception(const std::string& what); 19 | Exception(const char* what); 20 | }; 21 | 22 | template 23 | class StreamThrower { 24 | std::stringstream stream; 25 | bool triggered, moved = false; 26 | 27 | public: 28 | StreamThrower(bool cond = true, std::stringstream ss = std::stringstream()) 29 | : stream(std::move(ss)), triggered(cond) {} 30 | ~StreamThrower() noexcept(false) { 31 | if (triggered && !moved) { 32 | throw T(stream.str()); 33 | } 34 | } 35 | 36 | template 37 | StreamThrower operator<<(V&& s) { 38 | moved = true; 39 | stream << std::forward(s); 40 | return StreamThrower(triggered, std::move(stream)); 41 | } 42 | }; 43 | } // namespace rellic 44 | 45 | #define THROW_IF(cond) \ 46 | ::rellic::StreamThrower<::rellic::Exception>((cond)) \ 47 | << __FILE__ << ':' << __LINE__ << ' ' 48 | #define THROW() THROW_IF(true) 49 | #define CHECK_THROW(cond) THROW_IF(!(cond)) << "Check failed: " #cond " " -------------------------------------------------------------------------------- /include/rellic/Version.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Trail of Bits 2 | // Based on: 3 | // https://github.com/andrew-hardin/cmake-git-version-tracking/blob/master/better-example/git.h 4 | // Which is (C) 2020 Andrew Hardin 5 | // 6 | // MIT License 7 | // Copyright (c) 2020 Andrew Hardin 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #pragma once 29 | 30 | #include 31 | 32 | namespace rellic { 33 | namespace Version { 34 | 35 | bool HasVersionData(); 36 | bool HasUncommittedChanges(); 37 | std::string GetAuthorName(); 38 | std::string GetAuthorEmail(); 39 | std::string GetCommitHash(); 40 | std::string GetCommitDate(); 41 | std::string GetCommitSubject(); 42 | std::string GetCommitBody(); 43 | std::string GetVersionString(); 44 | 45 | } // namespace Version 46 | } // namespace rellic 47 | -------------------------------------------------------------------------------- /lib/AST/CondBasedRefine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "rellic/AST/CondBasedRefine.h" 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | namespace rellic { 17 | 18 | CondBasedRefine::CondBasedRefine(DecompilationContext &dec_ctx) 19 | : TransformVisitor(dec_ctx) {} 20 | 21 | bool CondBasedRefine::VisitCompoundStmt(clang::CompoundStmt *compound) { 22 | std::vector body{compound->body_begin(), compound->body_end()}; 23 | bool did_something{false}; 24 | 25 | for (size_t i{0}; i + 1 < body.size() && !did_something; ++i) { 26 | auto if_a{clang::dyn_cast(body[i])}; 27 | auto if_b{clang::dyn_cast(body[i + 1])}; 28 | 29 | // We need two `if` statements to combine 30 | if (!if_a || !if_b) { 31 | continue; 32 | } 33 | 34 | auto cond_a{dec_ctx.z3_exprs[dec_ctx.conds[if_a]]}; 35 | auto cond_b{dec_ctx.z3_exprs[dec_ctx.conds[if_b]]}; 36 | 37 | auto then_a{if_a->getThen()}; 38 | auto then_b{if_b->getThen()}; 39 | 40 | auto else_a{if_a->getElse()}; 41 | auto else_b{if_b->getElse()}; 42 | 43 | std::vector new_then_body{then_a}; 44 | clang::IfStmt *new_if{nullptr}; 45 | if (Prove(cond_a == cond_b)) { 46 | // We found two consecutive `if` statements with identical conditions, so 47 | // we can merge their `then` and `else` branches 48 | // 49 | // if(a) { X1; } else { Y1; } 50 | // if(a) { X2; } else { Y2; } 51 | // becomes 52 | // if(a) { X1; X2; } else { Y1; Y2; } 53 | new_then_body.push_back(then_b); 54 | auto new_then{dec_ctx.ast.CreateCompoundStmt(new_then_body)}; 55 | 56 | new_if = dec_ctx.ast.CreateIf(dec_ctx.marker_expr, new_then); 57 | 58 | if (else_a || else_b) { 59 | // At least one of the two `if` statements has an `else` branch 60 | std::vector new_else_body{}; 61 | 62 | if (else_a) { 63 | new_else_body.push_back(else_a); 64 | } 65 | 66 | if (else_b) { 67 | new_else_body.push_back(else_b); 68 | } 69 | 70 | auto new_else{dec_ctx.ast.CreateCompoundStmt(new_else_body)}; 71 | new_if->setElse(new_else); 72 | } 73 | 74 | did_something = true; 75 | } else if (Prove(cond_a == !cond_b)) { 76 | // We found two consecutive `if` statements with opposite conditions, so 77 | // we can append the else branch of the second to the then branch of the 78 | // first, and viceversa 79 | // 80 | // if(a) { X1; } else { Y1; } 81 | // if(!a) { X2; } else { Y2; } 82 | // becomes 83 | // if(a) { X1; Y2; } else { Y1; X2; } 84 | if (else_b) { 85 | new_then_body.push_back(else_b); 86 | } 87 | 88 | auto new_then{dec_ctx.ast.CreateCompoundStmt(new_then_body)}; 89 | 90 | std::vector new_else_body{}; 91 | if (else_a) { 92 | new_else_body.push_back(else_a); 93 | } 94 | new_else_body.push_back(then_b); 95 | 96 | new_if = dec_ctx.ast.CreateIf(dec_ctx.marker_expr, new_then); 97 | 98 | auto new_else{dec_ctx.ast.CreateCompoundStmt(new_else_body)}; 99 | new_if->setElse(new_else); 100 | 101 | did_something = true; 102 | } 103 | 104 | if (did_something) { 105 | dec_ctx.conds[new_if] = dec_ctx.conds[if_a]; 106 | body[i] = new_if; 107 | body.erase(std::next(body.begin(), i + 1)); 108 | } 109 | } 110 | if (did_something) { 111 | substitutions[compound] = dec_ctx.ast.CreateCompoundStmt(body); 112 | } 113 | return !Stopped(); 114 | } 115 | 116 | void CondBasedRefine::RunImpl() { 117 | LOG(INFO) << "Condition-based refinement"; 118 | TransformVisitor::RunImpl(); 119 | TraverseDecl(dec_ctx.ast_ctx.getTranslationUnitDecl()); 120 | } 121 | 122 | } // namespace rellic -------------------------------------------------------------------------------- /lib/AST/DeadStmtElim.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | #include "rellic/AST/DeadStmtElim.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace rellic { 14 | 15 | DeadStmtElim::DeadStmtElim(DecompilationContext &dec_ctx) 16 | : TransformVisitor(dec_ctx) {} 17 | 18 | bool DeadStmtElim::VisitIfStmt(clang::IfStmt *ifstmt) { 19 | // DLOG(INFO) << "VisitIfStmt"; 20 | bool can_delete = false; 21 | if (ifstmt->getCond() == dec_ctx.marker_expr) { 22 | can_delete = Prove(!dec_ctx.z3_exprs[dec_ctx.conds[ifstmt]]); 23 | } 24 | 25 | auto compound = clang::dyn_cast(ifstmt->getThen()); 26 | bool is_empty = compound ? compound->body_empty() : false; 27 | if (can_delete || is_empty) { 28 | substitutions[ifstmt] = nullptr; 29 | } 30 | return true; 31 | } 32 | 33 | bool DeadStmtElim::VisitCompoundStmt(clang::CompoundStmt *compound) { 34 | // DLOG(INFO) << "VisitCompoundStmt"; 35 | std::vector new_body; 36 | for (auto stmt : compound->body()) { 37 | // Filter out nullptr statements 38 | if (!stmt) { 39 | continue; 40 | } 41 | // Add only necessary statements 42 | if (auto expr = clang::dyn_cast(stmt)) { 43 | if (expr->HasSideEffects(dec_ctx.ast_ctx)) { 44 | new_body.push_back(stmt); 45 | } 46 | } else if (!clang::dyn_cast(stmt)) { 47 | new_body.push_back(stmt); 48 | } 49 | } 50 | // Create the a new compound 51 | if (changed || new_body.size() < compound->size()) { 52 | substitutions[compound] = dec_ctx.ast.CreateCompoundStmt(new_body); 53 | } 54 | return !Stopped(); 55 | } 56 | 57 | void DeadStmtElim::RunImpl() { 58 | LOG(INFO) << "Eliminating dead statements"; 59 | TransformVisitor::RunImpl(); 60 | TraverseDecl(dec_ctx.ast_ctx.getTranslationUnitDecl()); 61 | } 62 | 63 | } // namespace rellic 64 | -------------------------------------------------------------------------------- /lib/AST/InferenceRule.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "rellic/AST/InferenceRule.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace rellic { 15 | 16 | clang::Stmt *ApplyFirstMatchingRule( 17 | DecompilationContext &dec_ctx, clang::Stmt *stmt, 18 | std::vector> &rules) { 19 | clang::ast_matchers::MatchFinder::MatchFinderOptions opts; 20 | clang::ast_matchers::MatchFinder finder(opts); 21 | 22 | for (auto &rule : rules) { 23 | finder.addMatcher(rule->GetCondition(), rule.get()); 24 | } 25 | 26 | finder.match(*stmt, dec_ctx.ast_unit.getASTContext()); 27 | 28 | for (auto &rule : rules) { 29 | if (*rule) { 30 | return rule->GetOrCreateSubstitution(dec_ctx, stmt); 31 | } 32 | } 33 | 34 | return stmt; 35 | } 36 | 37 | } // namespace rellic -------------------------------------------------------------------------------- /lib/AST/LocalDeclRenamer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | #include "rellic/AST/LocalDeclRenamer.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace rellic { 18 | 19 | LocalDeclRenamer::LocalDeclRenamer(DecompilationContext &dec_ctx, 20 | IRToNameMap &names) 21 | : TransformVisitor(dec_ctx), 22 | seen_names(1), 23 | names(names) {} 24 | 25 | bool LocalDeclRenamer::IsNameVisible(const std::string &name) { 26 | for (auto &scope : seen_names) { 27 | if (scope.find(name) != scope.end()) { 28 | return true; 29 | } 30 | } 31 | return false; 32 | } 33 | 34 | bool LocalDeclRenamer::VisitVarDecl(clang::VarDecl *decl) { 35 | if (renamed_decls.find(decl) != renamed_decls.end()) { 36 | return !Stopped(); 37 | } 38 | renamed_decls.insert(decl); 39 | 40 | auto val{decls.find(decl)}; 41 | if (val == decls.end()) { 42 | return !Stopped(); 43 | } 44 | 45 | auto name{names.find(val->second)}; 46 | if (name == names.end()) { 47 | seen_names.back().insert(decl->getName().str()); 48 | return !Stopped(); 49 | } 50 | 51 | if (!IsNameVisible(name->second)) { 52 | seen_names.back().insert(name->second); 53 | decl->setDeclName(dec_ctx.ast.CreateIdentifier(name->second)); 54 | } else { 55 | // Append the automatically-generated name to the debug-info name in order 56 | // to avoid any lexical scoping issue 57 | // TODO(frabert): Recover proper lexical scoping from debug info metadata 58 | auto old_name{decl->getName().str()}; 59 | auto new_name{name->second + "_" + old_name}; 60 | decl->setDeclName(dec_ctx.ast.CreateIdentifier(new_name)); 61 | seen_names.back().insert(new_name); 62 | } 63 | 64 | return !Stopped(); 65 | } 66 | 67 | bool LocalDeclRenamer::TraverseFunctionDecl(clang::FunctionDecl *decl) { 68 | std::unordered_set scope; 69 | for (auto param : decl->parameters()) { 70 | scope.insert(param->getName().str()); 71 | } 72 | seen_names.push_back(scope); 73 | RecursiveASTVisitor::TraverseFunctionDecl(decl); 74 | seen_names.pop_back(); 75 | return !Stopped(); 76 | } 77 | 78 | bool LocalDeclRenamer::shouldTraversePostOrder() { return false; } 79 | 80 | void LocalDeclRenamer::RunImpl() { 81 | for (auto &pair : dec_ctx.value_decls) { 82 | decls[pair.second] = pair.first; 83 | } 84 | TraverseDecl(dec_ctx.ast_ctx.getTranslationUnitDecl()); 85 | } 86 | 87 | } // namespace rellic 88 | -------------------------------------------------------------------------------- /lib/AST/MaterializeConds.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "rellic/AST/MaterializeConds.h" 10 | 11 | #include 12 | #include 13 | 14 | #include "rellic/AST/Util.h" 15 | 16 | namespace rellic { 17 | 18 | MaterializeConds::MaterializeConds(DecompilationContext &dec_ctx) 19 | : TransformVisitor(dec_ctx), ast_gen(dec_ctx) {} 20 | 21 | bool MaterializeConds::VisitIfStmt(clang::IfStmt *stmt) { 22 | auto cond{dec_ctx.z3_exprs[dec_ctx.conds[stmt]]}; 23 | if (stmt->getCond() == dec_ctx.marker_expr) { 24 | stmt->setCond(ast_gen.ConvertExpr(cond)); 25 | } 26 | return true; 27 | } 28 | 29 | bool MaterializeConds::VisitWhileStmt(clang::WhileStmt *stmt) { 30 | auto cond{dec_ctx.z3_exprs[dec_ctx.conds[stmt]]}; 31 | if (stmt->getCond() == dec_ctx.marker_expr) { 32 | stmt->setCond(ast_gen.ConvertExpr(cond)); 33 | } 34 | return true; 35 | } 36 | 37 | bool MaterializeConds::VisitDoStmt(clang::DoStmt *stmt) { 38 | auto cond{dec_ctx.z3_exprs[dec_ctx.conds[stmt]]}; 39 | if (stmt->getCond() == dec_ctx.marker_expr) { 40 | stmt->setCond(ast_gen.ConvertExpr(cond)); 41 | } 42 | return true; 43 | } 44 | 45 | void MaterializeConds::RunImpl() { 46 | LOG(INFO) << "Materializing conditions"; 47 | TransformVisitor::RunImpl(); 48 | TraverseDecl(dec_ctx.ast_ctx.getTranslationUnitDecl()); 49 | } 50 | 51 | } // namespace rellic 52 | -------------------------------------------------------------------------------- /lib/AST/NestedScopeCombine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "rellic/AST/NestedScopeCombine.h" 10 | 11 | #include 12 | #include 13 | 14 | #include "rellic/AST/Util.h" 15 | 16 | namespace rellic { 17 | 18 | NestedScopeCombine::NestedScopeCombine(DecompilationContext &dec_ctx) 19 | : TransformVisitor(dec_ctx) {} 20 | 21 | bool NestedScopeCombine::VisitIfStmt(clang::IfStmt *ifstmt) { 22 | // DLOG(INFO) << "VisitIfStmt"; 23 | // Determine whether `cond` is a constant expression that is always true and 24 | // `ifstmt` should be replaced by `then` in it's parent nodes. 25 | auto cond{dec_ctx.z3_exprs[dec_ctx.conds[ifstmt]]}; 26 | if (Prove(cond)) { 27 | substitutions[ifstmt] = ifstmt->getThen(); 28 | } else if (ifstmt->getElse() && Prove(!cond)) { 29 | substitutions[ifstmt] = ifstmt->getElse(); 30 | } 31 | return !Stopped(); 32 | } 33 | 34 | bool NestedScopeCombine::VisitWhileStmt(clang::WhileStmt *stmt) { 35 | // Substitute while statements in the form `while(1) { sth; break; }` with 36 | // just `{ sth; }` 37 | auto cond{dec_ctx.z3_exprs[dec_ctx.conds[stmt]]}; 38 | if (Prove(cond)) { 39 | auto body{clang::cast(stmt->getBody())}; 40 | if (clang::isa(body->body_back())) { 41 | std::vector new_body{body->body_begin(), 42 | body->body_end() - 1}; 43 | substitutions[stmt] = dec_ctx.ast.CreateCompoundStmt(new_body); 44 | } 45 | } 46 | return !Stopped(); 47 | } 48 | 49 | bool NestedScopeCombine::VisitCompoundStmt(clang::CompoundStmt *compound) { 50 | // DLOG(INFO) << "VisitCompoundStmt"; 51 | bool has_compound = false; 52 | std::vector new_body; 53 | for (auto stmt : compound->body()) { 54 | if (auto child = clang::dyn_cast(stmt)) { 55 | new_body.insert(new_body.end(), child->body_begin(), child->body_end()); 56 | has_compound = true; 57 | } else { 58 | new_body.push_back(stmt); 59 | } 60 | } 61 | 62 | if (has_compound) { 63 | substitutions[compound] = dec_ctx.ast.CreateCompoundStmt(new_body); 64 | } 65 | 66 | return !Stopped(); 67 | } 68 | 69 | void NestedScopeCombine::RunImpl() { 70 | LOG(INFO) << "Combining nested scopes"; 71 | TransformVisitor::RunImpl(); 72 | TraverseDecl(dec_ctx.ast_ctx.getTranslationUnitDecl()); 73 | } 74 | 75 | } // namespace rellic 76 | -------------------------------------------------------------------------------- /lib/AST/ReachBasedRefine.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "rellic/AST/ReachBasedRefine.h" 10 | 11 | #include 12 | #include 13 | 14 | #include "rellic/AST/Util.h" 15 | 16 | namespace rellic { 17 | 18 | ReachBasedRefine::ReachBasedRefine(DecompilationContext &dec_ctx) 19 | : TransformVisitor(dec_ctx) {} 20 | 21 | bool ReachBasedRefine::VisitCompoundStmt(clang::CompoundStmt *compound) { 22 | std::vector body{compound->body_begin(), compound->body_end()}; 23 | std::vector ifs; 24 | z3::expr_vector conds{dec_ctx.z3_ctx}; 25 | 26 | auto ResetChain = [&]() { 27 | ifs.clear(); 28 | conds.resize(0); 29 | }; 30 | 31 | bool done_something{false}; 32 | for (size_t i{0}; i < body.size() && !done_something; ++i) { 33 | auto if_stmt{clang::dyn_cast(body[i])}; 34 | if (!if_stmt) { 35 | ResetChain(); 36 | continue; 37 | } 38 | 39 | ifs.push_back(if_stmt); 40 | auto cond{dec_ctx.z3_exprs[dec_ctx.conds[if_stmt]]}; 41 | 42 | if (if_stmt->getElse()) { 43 | // We cannot link `if` statements that contain `else` branches 44 | ResetChain(); 45 | continue; 46 | } 47 | 48 | // Is the current `if` statement unreachable from all the others? 49 | bool is_unreachable{Prove(HeavySimplify(!(cond && z3::mk_or(conds))))}; 50 | 51 | if (!is_unreachable) { 52 | ResetChain(); 53 | continue; 54 | } 55 | 56 | conds.push_back(cond); 57 | 58 | // Do the collected statements cover all possibilities? 59 | auto is_complete{Prove(HeavySimplify(z3::mk_or(conds)))}; 60 | 61 | if (ifs.size() <= 2 || !is_complete) { 62 | // We need to collect more statements 63 | continue; 64 | } 65 | 66 | /* 67 | `body` will look like this at this point: 68 | 69 | ... 70 | i - n : ... 71 | i - n + 1: if(cond_1) { } 72 | i - n + 2: if(cond_2) { } 73 | ... 74 | i - 1 : if(cond_n-1) { } 75 | i : if(cond_n) { } 76 | ... 77 | 78 | and we want to chain all of the statements together: 79 | ... 80 | i - n : ... 81 | i - n + 1: if(cond_1) { } else if(cond_2) { } ... else if(cond_n) { } 82 | i - n + 2: if(cond_2) { } else if(cond_3) { } ... else if(cond_n) { } 83 | ... 84 | i - 1 : if(cond_n-1) { } else { } 85 | i : if(cond_n) { } 86 | ... 87 | */ 88 | auto last_if{ifs[0]}; 89 | for (auto stmt : ifs) { 90 | if (stmt == ifs.front()) { 91 | continue; 92 | } 93 | if (stmt == ifs.back()) { 94 | last_if->setElse(stmt->getThen()); 95 | } else { 96 | last_if->setElse(stmt); 97 | last_if = stmt; 98 | } 99 | } 100 | 101 | /* 102 | `body` will look like this at this point: 103 | 104 | ... 105 | i - n : ... 106 | i - n + 1: if(cond_1) { } else if(cond_2) { } ... else if(cond_n) { } 107 | i - n + 2: if(cond_2) { } else if(cond_3) { } ... else if(cond_n) { } 108 | ... 109 | i - 1 : if(cond_n-1) { } else if(cond_n) { } 110 | i : if(cond_n) { } 111 | ... 112 | 113 | but since we chained all of the statements into the first, we want to remove 114 | the others from the body: 115 | 116 | ... 117 | i - n : ... 118 | i - n + 1: if(cond_1) { } else if(cond_2) { } else if ... 119 | ... 120 | */ 121 | size_t start_delete{i - (ifs.size() - 2)}; 122 | size_t end_delete{i}; 123 | body.erase(body.erase(std::next(body.begin(), start_delete), 124 | std::next(body.begin(), end_delete))); 125 | done_something = true; 126 | } 127 | 128 | if (done_something) { 129 | substitutions[compound] = dec_ctx.ast.CreateCompoundStmt(body); 130 | } 131 | return !Stopped(); 132 | } 133 | 134 | void ReachBasedRefine::RunImpl() { 135 | LOG(INFO) << "Reachability-based refinement"; 136 | TransformVisitor::RunImpl(); 137 | TraverseDecl(dec_ctx.ast_ctx.getTranslationUnitDecl()); 138 | } 139 | 140 | } // namespace rellic -------------------------------------------------------------------------------- /lib/AST/StructFieldRenamer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | #include "rellic/AST/StructFieldRenamer.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace rellic { 19 | 20 | StructFieldRenamer::StructFieldRenamer(DecompilationContext &dec_ctx, 21 | IRTypeToDITypeMap &types) 22 | : ASTPass(dec_ctx), types(types) {} 23 | 24 | bool StructFieldRenamer::VisitRecordDecl(clang::RecordDecl *decl) { 25 | auto type{decls[decl]}; 26 | CHECK(type) << "Type information not present for declaration"; 27 | 28 | auto di{types[type]}; 29 | if (!di) { 30 | return !Stopped(); 31 | } 32 | 33 | auto ditype = llvm::cast(di); 34 | std::vector decl_fields; 35 | std::vector di_fields; 36 | 37 | for (auto field : decl->fields()) { 38 | decl_fields.push_back(field); 39 | } 40 | 41 | for (auto field : ditype->getElements()) { 42 | di_fields.push_back(llvm::cast(field)); 43 | } 44 | 45 | std::unordered_set seen_names; 46 | 47 | for (auto i{0U}; i < decl_fields.size(); ++i) { 48 | auto decl_field{decl_fields[i]}; 49 | auto di_field{di_fields[i]}; 50 | 51 | // FIXME(frabert): Is a clash between field names actually possible? 52 | // Can this mechanism actually be left out? 53 | auto name{di_field->getName().str()}; 54 | if (seen_names.find(name) == seen_names.end()) { 55 | seen_names.insert(name); 56 | decl_field->setDeclName(dec_ctx.ast.CreateIdentifier(name)); 57 | changed = true; 58 | } else { 59 | auto old_name{decl_field->getName().str()}; 60 | decl_field->setDeclName( 61 | dec_ctx.ast.CreateIdentifier(name + "_" + old_name)); 62 | changed = true; 63 | } 64 | } 65 | 66 | return !Stopped(); 67 | } 68 | 69 | void StructFieldRenamer::RunImpl() { 70 | LOG(INFO) << "Renaming struct fields"; 71 | for (auto &pair : dec_ctx.type_decls) { 72 | decls[pair.second] = pair.first; 73 | } 74 | TraverseDecl(dec_ctx.ast_ctx.getTranslationUnitDecl()); 75 | } 76 | 77 | } // namespace rellic 78 | -------------------------------------------------------------------------------- /lib/AST/SubprogramGenerator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | #define GOOGLE_STRIP_LOG 1 9 | 10 | #include "rellic/AST/SubprogramGenerator.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace rellic { 16 | SubprogramGenerator::SubprogramGenerator(clang::ASTUnit& ast_unit, 17 | StructGenerator& struct_gen) 18 | : ast_ctx(ast_unit.getASTContext()), 19 | struct_gen(struct_gen), 20 | ast(ast_unit) {} 21 | 22 | clang::FunctionDecl* SubprogramGenerator::VisitSubprogram( 23 | llvm::DISubprogram* subp) { 24 | std::string name{}; 25 | auto linkageName{subp->getLinkageName().str()}; 26 | if (linkageName == "") { 27 | name = subp->getName(); 28 | } else { 29 | name = linkageName; 30 | } 31 | CHECK_NE(name, ""); 32 | DLOG(INFO) << "Visiting subprogram " << name; 33 | 34 | auto type{struct_gen.GetType(subp->getType())}; 35 | auto tudecl{ast_ctx.getTranslationUnitDecl()}; 36 | auto fdecl{ast.CreateFunctionDecl(tudecl, type, name)}; 37 | auto type_arr{subp->getType()->getTypeArray()}; 38 | std::vector params{}; 39 | for (auto i{1U}; i < type_arr.size(); i++) { 40 | if (!type_arr[i]) { 41 | break; 42 | } 43 | // TODO(frabert): Extract names from bitcode if available 44 | auto parmname{"arg" + std::to_string(i)}; 45 | params.push_back( 46 | ast.CreateParamDecl(fdecl, struct_gen.GetType(type_arr[i]), parmname)); 47 | } 48 | fdecl->setParams(params); 49 | tudecl->addDecl(fdecl); 50 | return fdecl; 51 | } 52 | 53 | } // namespace rellic -------------------------------------------------------------------------------- /lib/AST/Z3CondSimplify.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "rellic/AST/Z3CondSimplify.h" 10 | 11 | #include 12 | #include 13 | 14 | #include "rellic/AST/Util.h" 15 | 16 | namespace rellic { 17 | 18 | Z3CondSimplify::Z3CondSimplify(DecompilationContext& dec_ctx) 19 | : ASTPass(dec_ctx) {} 20 | 21 | void Z3CondSimplify::RunImpl() { 22 | LOG(INFO) << "Simplifying conditions using Z3"; 23 | for (size_t i{0}; i < dec_ctx.z3_exprs.size() && !Stopped(); ++i) { 24 | auto simpl{OrderById(dec_ctx.z3_exprs[i].simplify())}; 25 | dec_ctx.z3_exprs.set(i, simpl); 26 | } 27 | } 28 | 29 | } // namespace rellic 30 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | # Based on: https://github.com/andrew-hardin/cmake-git-version-tracking/blob/master/better-example/CMakeLists.txt 10 | # By Andrew Hardin 11 | # Released under the MIT License. 12 | # https://raw.githubusercontent.com/andrew-hardin/cmake-git-version-tracking/master/LICENSE 13 | # 14 | # Define the two required variables before including 15 | # the source code for watching a git repository. 16 | set(PRE_CONFIGURE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Version.cpp.in") 17 | set(POST_CONFIGURE_FILE "${CMAKE_CURRENT_BINARY_DIR}/Version.cpp") 18 | set(GIT_WORKING_DIR "${PROJECT_SOURCE_DIR}") 19 | include("${PROJECT_SOURCE_DIR}/cmake/git_watcher.cmake") 20 | 21 | set(include_dir "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}") 22 | 23 | set(AST_HEADERS 24 | "${include_dir}/AST/ASTBuilder.h" 25 | "${include_dir}/AST/CXXToCDecl.h" 26 | "${include_dir}/AST/CondBasedRefine.h" 27 | "${include_dir}/AST/DeadStmtElim.h" 28 | "${include_dir}/AST/DebugInfoCollector.h" 29 | "${include_dir}/AST/ExprCombine.h" 30 | "${include_dir}/AST/GenerateAST.h" 31 | "${include_dir}/AST/IRToASTVisitor.h" 32 | "${include_dir}/AST/InferenceRule.h" 33 | "${include_dir}/AST/LocalDeclRenamer.h" 34 | "${include_dir}/AST/LoopRefine.h" 35 | "${include_dir}/AST/MaterializeConds.h" 36 | "${include_dir}/AST/NestedCondProp.h" 37 | "${include_dir}/AST/NestedScopeCombine.h" 38 | "${include_dir}/AST/ReachBasedRefine.h" 39 | "${include_dir}/AST/StructFieldRenamer.h" 40 | "${include_dir}/AST/StructGenerator.h" 41 | "${include_dir}/AST/SubprogramGenerator.h" 42 | "${include_dir}/AST/TransformVisitor.h" 43 | "${include_dir}/AST/TypeProvider.h" 44 | "${include_dir}/AST/Util.h" 45 | "${include_dir}/AST/Z3CondSimplify.h" 46 | ) 47 | 48 | set(BC_HEADERS 49 | "${include_dir}/BC/Util.h" 50 | "${include_dir}/BC/Version.h" 51 | ) 52 | 53 | set(VERSION_HEADERS 54 | "${include_dir}/Version.h" 55 | ) 56 | 57 | set(public_HEADERS 58 | ${AST_HEADERS} 59 | ${BC_HEADERS} 60 | ${VERSION_HEADERS} 61 | ) 62 | 63 | set(AST_SOURCES 64 | AST/ASTBuilder.cpp 65 | AST/CXXToCDecl.cpp 66 | AST/InferenceRule.cpp 67 | AST/DeadStmtElim.cpp 68 | AST/DebugInfoCollector.cpp 69 | AST/CondBasedRefine.cpp 70 | AST/ExprCombine.cpp 71 | AST/GenerateAST.cpp 72 | AST/IRToASTVisitor.cpp 73 | AST/LocalDeclRenamer.cpp 74 | AST/LoopRefine.cpp 75 | AST/MaterializeConds.cpp 76 | AST/NestedCondProp.cpp 77 | AST/NestedScopeCombine.cpp 78 | AST/Util.cpp 79 | AST/Z3CondSimplify.cpp 80 | AST/ReachBasedRefine.cpp 81 | AST/StructFieldRenamer.cpp 82 | AST/StructGenerator.cpp 83 | AST/SubprogramGenerator.cpp 84 | AST/TypeProvider.cpp 85 | ) 86 | 87 | set(BC_SOURCES 88 | BC/Util.cpp 89 | ) 90 | 91 | 92 | add_library("${PROJECT_NAME}" STATIC 93 | ${public_HEADERS} 94 | ${AST_SOURCES} 95 | ${BC_SOURCES} 96 | 97 | Dec2Hex.cpp 98 | Decompiler.cpp 99 | Exception.cpp 100 | 101 | "${POST_CONFIGURE_FILE}" # Version.cpp 102 | ) 103 | 104 | 105 | set_target_properties( 106 | "${PROJECT_NAME}" PROPERTIES 107 | VISIBILITY_INLINES_HIDDEN YES 108 | CXX_VISIBILITY_PRESET hidden 109 | ) 110 | 111 | #link version information 112 | target_link_libraries("${PROJECT_NAME}" 113 | PUBLIC 114 | "${PROJECT_NAME}_cxx_settings" 115 | glog::glog 116 | z3::libz3 117 | "${llvm_libs}" 118 | clangIndex 119 | clangCodeGen 120 | clangASTMatchers 121 | clangTooling 122 | ) 123 | 124 | set_target_properties("${PROJECT_NAME}" 125 | PROPERTIES 126 | LINKER_LANGUAGE 127 | CXX 128 | ) 129 | 130 | target_include_directories("${PROJECT_NAME}" 131 | PUBLIC 132 | $ 133 | $ 134 | ) 135 | 136 | if(RELLIC_ENABLE_INSTALL) 137 | include(GNUInstallDirs) 138 | install( 139 | DIRECTORY 140 | "${include_dir}" 141 | DESTINATION 142 | ${CMAKE_INSTALL_INCLUDEDIR}) 143 | 144 | 145 | install( 146 | TARGETS 147 | "${PROJECT_NAME}" 148 | EXPORT 149 | "${PROJECT_NAME}Targets" 150 | RUNTIME 151 | DESTINATION 152 | ${CMAKE_INSTALL_BINDIR} 153 | LIBRARY 154 | DESTINATION 155 | ${CMAKE_INSTALL_LIBDIR} 156 | ARCHIVE 157 | DESTINATION 158 | ${CMAKE_INSTALL_LIBDIR} 159 | ) 160 | endif(RELLIC_ENABLE_INSTALL) 161 | -------------------------------------------------------------------------------- /lib/Dec2Hex.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace rellic { 19 | namespace { 20 | using namespace clang; 21 | using namespace clang::ast_matchers; 22 | 23 | StatementMatcher intlit = integerLiteral().bind("intlit"); 24 | 25 | class IntegerReplacer : public MatchFinder::MatchCallback { 26 | Rewriter &rw; 27 | std::function shouldConvert; 28 | 29 | public: 30 | IntegerReplacer(Rewriter &rw, 31 | std::function shouldConvert) 32 | : rw(rw), shouldConvert(shouldConvert) {} 33 | virtual void run(const MatchFinder::MatchResult &Result) { 34 | if (auto lit = Result.Nodes.getNodeAs("intlit")) { 35 | if (!shouldConvert(lit->getValue())) { 36 | return; 37 | } 38 | 39 | llvm::SmallString<40> str; 40 | lit->getValue().toString( 41 | str, /*radix=*/16, /*isSigned=*/lit->getType()->isSignedIntegerType(), 42 | /*formatAsCLiteral=*/true); 43 | std::string res; 44 | llvm::raw_string_ostream OS(res); 45 | OS << str; 46 | switch (lit->getType()->castAs()->getKind()) { 47 | default: 48 | llvm_unreachable("Unexpected type for integer literal!"); 49 | case BuiltinType::Char_S: 50 | case BuiltinType::Char_U: 51 | OS << "i8"; 52 | break; 53 | case BuiltinType::UChar: 54 | OS << "Ui8"; 55 | break; 56 | case BuiltinType::Short: 57 | OS << "i16"; 58 | break; 59 | case BuiltinType::UShort: 60 | OS << "Ui16"; 61 | break; 62 | case BuiltinType::Int: 63 | break; // no suffix. 64 | case BuiltinType::UInt: 65 | OS << 'U'; 66 | break; 67 | case BuiltinType::Long: 68 | OS << 'L'; 69 | break; 70 | case BuiltinType::ULong: 71 | OS << "UL"; 72 | break; 73 | case BuiltinType::LongLong: 74 | OS << "LL"; 75 | break; 76 | case BuiltinType::ULongLong: 77 | OS << "ULL"; 78 | break; 79 | } 80 | rw.ReplaceText(lit->getSourceRange(), res); 81 | } 82 | } 83 | }; 84 | } // namespace 85 | 86 | void ConvertIntegerLiteralsToHex( 87 | clang::ASTContext &ast_ctx, llvm::raw_ostream &os, 88 | std::function shouldConvert) { 89 | auto &sm{ast_ctx.getSourceManager()}; 90 | clang::Rewriter rewriter{sm, ast_ctx.getLangOpts()}; 91 | clang::ast_matchers::MatchFinder finder; 92 | IntegerReplacer replacer{rewriter, shouldConvert}; 93 | 94 | finder.addMatcher(intlit, &replacer); 95 | finder.matchAST(ast_ctx); 96 | rewriter.getRewriteBufferFor(sm.getMainFileID())->write(os); 97 | } 98 | } // namespace rellic 99 | -------------------------------------------------------------------------------- /lib/Exception.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | #include "rellic/Exception.h" 9 | 10 | namespace rellic { 11 | Exception::Exception(const std::string& what) : std::runtime_error(what) {} 12 | Exception::Exception(const char* what) : std::runtime_error(what) {} 13 | } // namespace rellic -------------------------------------------------------------------------------- /lib/Version.cpp.in: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Trail of Bits 2 | // Based on: https://github.com/andrew-hardin/cmake-git-version-tracking/blob/master/better-example/git.cc.in 3 | // Which is (C) 2020 Andrew Hardin 4 | // 5 | // MIT License 6 | // Copyright (c) 2020 Andrew Hardin 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | // 26 | #include "rellic/Version.h" 27 | 28 | namespace rellic { 29 | namespace Version { 30 | 31 | bool HasVersionData() { 32 | return @GIT_RETRIEVED_STATE@; 33 | } 34 | bool HasUncommittedChanges() { 35 | return @GIT_IS_DIRTY@; 36 | } 37 | std::string GetAuthorName() { 38 | return "@GIT_AUTHOR_NAME@"; 39 | } 40 | std::string GetAuthorEmail() { 41 | return "@GIT_AUTHOR_EMAIL@"; 42 | } 43 | std::string GetCommitHash() { 44 | return "@GIT_HEAD_SHA1@"; 45 | } 46 | std::string GetCommitDate() { 47 | return "@GIT_COMMIT_DATE_ISO8601@"; 48 | } 49 | std::string GetCommitSubject() { 50 | return "@GIT_COMMIT_SUBJECT@"; 51 | } 52 | std::string GetCommitBody() { 53 | return "@GIT_COMMIT_BODY@"; 54 | } 55 | std::string GetVersionString() { 56 | return "@VERSION_STRING@"; 57 | } 58 | 59 | 60 | } // namespace Version 61 | } // namespace rellic 62 | -------------------------------------------------------------------------------- /packaging/README.md: -------------------------------------------------------------------------------- 1 | # rellic packaging scripts 2 | 3 | ## How to generate packages 4 | 5 | 1. Configure and build rellic 6 | 2. Set the **DESTDIR** variable to a new folder 7 | 3. Run the packaging script, passing the **DESTDIR** folder 8 | 9 | Example: 10 | 11 | ```sh 12 | rellic_version=$(git describe --always) 13 | 14 | cpack -D RELLIC_DATA_PATH="/path/to/install/directory" \ 15 | -R ${rellic_version} \ 16 | --config "packaging/main.cmake" 17 | ``` 18 | -------------------------------------------------------------------------------- /packaging/cmake/dispatcher.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}") 10 | set(CPACK_INSTALLED_DIRECTORIES "${RELLIC_DATA_PATH};.") 11 | 12 | string(TOLOWER "${CMAKE_SYSTEM_NAME}" system_name) 13 | if(system_name STREQUAL "darwin") 14 | set(system_name "macos") 15 | endif() 16 | 17 | set(common_include "${CMAKE_CURRENT_LIST_DIR}/system/${system_name}/common.cmake") 18 | if(EXISTS "${common_include}") 19 | include("${common_include}") 20 | endif() 21 | 22 | string(TOLOWER "${CPACK_GENERATOR}" cpack_generator) 23 | include("${CMAKE_CURRENT_LIST_DIR}/system/${system_name}/generators/${cpack_generator}.cmake") 24 | -------------------------------------------------------------------------------- /packaging/cmake/system/linux/generators/deb.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | set(CPACK_DEBIAN_PACKAGE_PRIORITY "extra") 10 | set(CPACK_DEBIAN_PACKAGE_SECTION "default") 11 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "${CPACK_PACKAGE_HOMEPAGE_URL}") 12 | 13 | -------------------------------------------------------------------------------- /packaging/cmake/system/linux/generators/rpm.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | set(CPACK_RPM_PACKAGE_RELEASE "${CPACK_PACKAGE_VERSION}") 10 | set(CPACK_RPM_FILE_NAME "RPM-DEFAULT") 11 | set(CPACK_RPM_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}") 12 | set(CPACK_RPM_PACKAGE_GROUP "default") 13 | set(CPACK_RPM_PACKAGE_LICENSE "GNU Affero General Public License v3.0") 14 | -------------------------------------------------------------------------------- /packaging/cmake/system/linux/generators/tgz.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | set(CPACK_SET_DESTDIR ON) 10 | set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) 11 | -------------------------------------------------------------------------------- /packaging/cmake/system/macos/generators/tgz.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | set(CPACK_SET_DESTDIR ON) 10 | set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) 11 | -------------------------------------------------------------------------------- /packaging/main.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") 10 | set(CPACK_GENERATOR "TGZ;DEB;RPM") 11 | 12 | elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") 13 | set(CPACK_GENERATOR "TGZ") 14 | endif() 15 | 16 | if(RELLIC_DATA_PATH STREQUAL "") 17 | message(FATAL_ERROR "The RELLIC_DATA_PATH variable was not set") 18 | endif() 19 | 20 | if(RELLIC_PACKAGE_VERSION STREQUAL "") 21 | message(FATAL_ERROR "The RELLIC_PACKAGE_VERSION variable was not set") 22 | endif() 23 | 24 | set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_CURRENT_LIST_DIR}/cmake/dispatcher.cmake") 25 | 26 | set(CPACK_PACKAGE_DESCRIPTION "Rellic produces goto-free C output from LLVM bitcode") 27 | set(CPACK_PACKAGE_NAME "rellic") 28 | set(CPACK_PACKAGE_VENDOR "Trail of Bits") 29 | set(CPACK_PACKAGE_CONTACT "opensource@trailofbits.com") 30 | set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/lifting-bits/rellic") 31 | -------------------------------------------------------------------------------- /rellicConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | @PACKAGE_INIT@ 10 | 11 | if(NOT TARGET rellic::rellic) 12 | 13 | include(CMakeFindDependencyMacro) 14 | find_dependency(gflags) 15 | find_dependency(glog) 16 | find_dependency(Z3) 17 | find_dependency(LLVM) 18 | find_dependency(Clang) 19 | 20 | # Exported Targets 21 | 22 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 23 | check_required_components("@PROJECT_NAME@") 24 | 25 | endif() 26 | -------------------------------------------------------------------------------- /scripts/build-preset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 3 | PROJECT=rellic 4 | 5 | BUILDLOG=${PROJECT}-build.log 6 | CONFIGLOG=${PROJECT}-configure.log 7 | rm -f ${BUILDLOG} ${CONFIGLOG} 8 | BUILD_TYPE=dbg 9 | VCPKG_SUFFIX="-rel" 10 | 11 | set -o pipefail 12 | 13 | function sanity_check { 14 | if [ -z "${CMAKE_TOOLCHAIN_FILE}" ]; then 15 | echo "Please set the CMAKE_TOOLCHAIN_FILE environment variable to the CMake toolchain file to build against" 16 | exit 1 17 | else 18 | echo "Building against CMake toolchain file: [${CMAKE_TOOLCHAIN_FILE}]" 19 | fi 20 | 21 | if [ -z "${INSTALL_DIR}" ]; then 22 | echo "Please set the INSTALL_DIR environment variable to the desired installation directory" 23 | exit 1 24 | else 25 | echo "Installing to: [${INSTALL_DIR}]" 26 | fi 27 | } 28 | 29 | function show_usage { 30 | 31 | printf "${0}: Build ${PROJECT} [-- extra arguments to CMake]" 32 | printf "\n" 33 | printf "\t--help: this screen\n" 34 | printf "\t--debug-vcpkg: build against a debug vcpkg (default OFF)\n" 35 | printf "\t: the type of build to do (debug or release or asan+debug)\n" 36 | printf "\tArguments after '--' are passed to CMake during configuration (e.g. -DCMAKE_C_COMPILER=foo)\n" 37 | printf "\n" 38 | printf "INSTALL_DIR set to [${INSTALL_DIR}]\n" 39 | printf "CMAKE_TOOLCHAIN_FILE set to [${CMAKE_TOOLCHAIN_FILE}]\n" 40 | 41 | return 0 42 | } 43 | 44 | function set_arch { 45 | local arch=$(uname -m) 46 | case ${arch} in 47 | aarch64 | arm64) 48 | echo "arm64" 49 | ;; 50 | x86_64) 51 | echo "x64" 52 | ;; 53 | *) 54 | echo "Unknown architecture: ${arch}" 55 | exit 1 56 | esac 57 | } 58 | 59 | function set_os { 60 | local os=$(uname -s) 61 | case ${os} in 62 | Darwin) 63 | echo "osx" 64 | ;; 65 | Linux) 66 | echo "linux" 67 | ;; 68 | *) 69 | echo "Unknown OS: ${os}" 70 | exit 1 71 | esac 72 | } 73 | 74 | 75 | # Make the user specify which build type 76 | if [[ $# -eq 0 ]]; then 77 | show_usage ${0} 78 | exit 0 79 | fi 80 | 81 | # check if proper env vars are set 82 | sanity_check 83 | 84 | # Look for help or set the build type 85 | while [[ $# -gt 0 ]] 86 | do 87 | key="$1" 88 | case $key in 89 | --help | -h | "-?") 90 | show_usage ${0} 91 | exit 0 92 | ;; 93 | --debug-vcpkg) 94 | VCPKG_SUFFIX="" 95 | shift 96 | ;; 97 | debug) 98 | BUILD_TYPE="dbg" 99 | shift 100 | ;; 101 | release) 102 | BUILD_TYPE="rel" 103 | shift 104 | ;; 105 | asan) 106 | BUILD_TYPE="asan" 107 | VCPKG_SUFFIX="-asan" 108 | shift 109 | ;; 110 | "--") 111 | shift 112 | break 113 | ;; 114 | *) # unknown option 115 | echo "UNKNOWN OPTION: ${1}" 116 | echo "Usage:" 117 | show_usage ${0} 118 | exit 1 119 | ;; 120 | esac 121 | done 122 | 123 | ARCH=$(set_arch) 124 | OS=$(set_os) 125 | export VCPKG_TARGET_TRIPLET=${ARCH}-${OS}${VCPKG_SUFFIX} 126 | 127 | echo "Configuring [${BUILD_TYPE}] [${ARCH}] against vcpkg [${VCPKG_TARGET_TRIPLET}]..." 128 | if [[ "${@}" != "" ]] 129 | then 130 | echo "Passing extra arguments to CMake: ${@}" 131 | fi 132 | 133 | cmake --preset vcpkg-${ARCH}-${BUILD_TYPE} ${@} &>${CONFIGLOG} 134 | if [ "$?" != "0" ]; then 135 | echo "Configuration failed. See ${CONFIGLOG}" 136 | cat "${CONFIGLOG}" 137 | exit 1 138 | else 139 | echo "Configure success!" 140 | fi 141 | 142 | echo "Building [${BUILD_TYPE}] [${ARCH}]..." 143 | cmake --build --preset ${ARCH}-${BUILD_TYPE} --parallel &>${BUILDLOG} 144 | if [ "$?" != "0" ]; then 145 | echo "Build failed. See ${BUILDLOG}" 146 | cat "${BUILDLOG}" 147 | exit 1 148 | else 149 | echo "Build success!" 150 | fi 151 | 152 | echo "Installing [${BUILD_TYPE}] [${ARCH}]..." 153 | # re-use build log since its mostly a part of build process 154 | cmake --build --preset ${ARCH}-${BUILD_TYPE} --target install --parallel >>${BUILDLOG} 2>&1 155 | if [ "$?" != "0" ]; then 156 | echo "Install failed. See ${BUILDLOG}" 157 | cat "${BUILDLOG}" 158 | exit 1 159 | else 160 | echo "Install success!" 161 | fi 162 | -------------------------------------------------------------------------------- /scripts/decompile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import unittest 4 | import subprocess 5 | import argparse 6 | import tempfile 7 | import os 8 | import sys 9 | 10 | 11 | class RunError(Exception): 12 | def __init__(self, msg): 13 | self.msg = msg 14 | 15 | def __str__(self): 16 | return str(self.msg) 17 | 18 | 19 | def run_cmd(cmd, timeout): 20 | try: 21 | p = subprocess.run( 22 | cmd, 23 | stdout=subprocess.PIPE, 24 | stderr=subprocess.PIPE, 25 | timeout=timeout, 26 | universal_newlines=True, 27 | ) 28 | except FileNotFoundError as e: 29 | raise RunError('Error: No such file or directory: "' + e.filename + '"') 30 | except PermissionError as e: 31 | raise RunError('Error: File "' + e.filename + '" is not an executable.') 32 | 33 | return p 34 | 35 | 36 | def decompile(self, rellic, input, output, timeout): 37 | cmd = [rellic] 38 | cmd.extend( 39 | ["--input", input, "--output", output] 40 | ) 41 | p = run_cmd(cmd, timeout) 42 | 43 | self.assertEqual(p.returncode, 0, "rellic-decomp failure: %s" % p.stderr) 44 | self.assertEqual( 45 | len(p.stderr), 0, "errors or warnings during decompilation: %s" % p.stderr 46 | ) 47 | 48 | return p 49 | 50 | class TestDecompile(unittest.TestCase): 51 | pass 52 | 53 | 54 | if __name__ == "__main__": 55 | parser = argparse.ArgumentParser() 56 | parser.add_argument("rellic", help="path to rellic-decomp") 57 | parser.add_argument("tests", help="path to test directory") 58 | parser.add_argument("-t", "--timeout", help="set timeout in seconds", type=int) 59 | 60 | args = parser.parse_args() 61 | 62 | def test_generator(path): 63 | def test(self): 64 | with tempfile.TemporaryDirectory() as tempdir: 65 | rt_c = os.path.join(tempdir, "rt.c") 66 | decompile(self, args.rellic, path, rt_c, args.timeout) 67 | 68 | return test 69 | 70 | for item in os.scandir(args.tests): 71 | if item.is_file(): 72 | name, ext = os.path.splitext(item.name) 73 | # Allow for READMEs and data/headers 74 | if ext in [".bc ", ".ll"]: 75 | test_name = f"test_{name}" 76 | test = test_generator(item.path) 77 | setattr(TestDecompile, test_name, test) 78 | 79 | unittest.main(argv=[sys.argv[0]]) 80 | -------------------------------------------------------------------------------- /scripts/docker-decomp-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Needed to process multiple arguments to docker image 4 | 5 | /opt/trailofbits/bin/rellic-decomp "$@" 6 | -------------------------------------------------------------------------------- /scripts/generate_changelog.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | PROJECT_NAME="rellic" 4 | 5 | main() { 6 | if [[ $# != 1 ]] ; then 7 | printf "Usage:\n\tgenerate_changelog.sh \n" 8 | return 1 9 | fi 10 | 11 | local output_path="${1}" 12 | local current_version="$(git describe --tags --always)" 13 | local previous_version="$(git describe --tags --always --abbrev=0 ${current_version}^)" 14 | 15 | echo "Current version: ${current_version}" 16 | echo "Previous version: ${previous_version}" 17 | echo "Output file: ${output_path}" 18 | 19 | printf "# Changelog\n\n" > "${output_path}" 20 | printf "The following are the changes that happened between versions ${previous_version} and ${current_version}\n\n" >> "${output_path}" 21 | 22 | git log ${previous_version}...${current_version} \ 23 | --pretty=format:" * [%h](http://github.com/lifting-bits/${PROJECT_NAME}/commit/%H) - %s" \ 24 | --reverse | grep -v 'Merge branch' >> "${output_path}" 25 | 26 | return 0 27 | } 28 | 29 | main $@ 30 | exit $? 31 | -------------------------------------------------------------------------------- /scripts/requirements.txt: -------------------------------------------------------------------------------- 1 | # test-angha-1k.sh python requirements 2 | tqdm 3 | requests 4 | -------------------------------------------------------------------------------- /scripts/run-on-anghabench.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #These are filled in by the CI system 4 | export RELLIC_BRANCH=__RELLIC_BRANCH__ 5 | export RUN_SIZE=__RUN_SIZE__ 6 | 7 | export LLVM_VERSION=13 8 | export CC=clang-${LLVM_VERSION} CXX=clang++-${LLVM_VERSION} 9 | 10 | 11 | echo "Saving output to $(pwd)/build.log" 12 | 13 | { 14 | apt-get update 15 | apt-get install -yqq s3cmd pixz curl git python3 python3-pip xz-utils cmake ninja-build clang-${LLVM_VERSION} 16 | python3 -m pip install requests 17 | #install new cmake 18 | curl -LO https://github.com/Kitware/CMake/releases/download/v3.22.1/cmake-3.22.1-linux-x86_64.sh 19 | sh ./cmake-3.22.1-linux-x86_64.sh --skip-license --prefix=/usr 20 | } &>> build.log 21 | 22 | { 23 | git clone --recursive --shallow-submodules --depth=1 -b ${RELLIC_BRANCH} https://github.com/lifting-bits/rellic rellic 24 | # CI Branch is defined by the CI system 25 | git clone --recursive --shallow-submodules --depth=1 -b ${CI_BRANCH} https://github.com/lifting-bits/lifting-tools-ci ci 26 | } &>> build.log 27 | 28 | { 29 | pushd rellic 30 | # build us a rellic 31 | scripts/build.sh \ 32 | --install \ 33 | --llvm-version ${LLVM_VERSION} \ 34 | --extra-cmake-args "-DCMAKE_BUILD_TYPE=Release" 35 | if [[ "$?" != "0" ]] 36 | then 37 | exit 1 38 | fi 39 | popd 40 | } &>> build.log 41 | 42 | { 43 | pushd ci 44 | # Install extra requirements if needed 45 | if [[ -f requirements.txt ]] 46 | then 47 | python3 -m pip install -r requirements.txt 48 | fi 49 | 50 | mkdir -p $(pwd)/decompiled 51 | mkdir -p $(pwd)/recompiled 52 | 53 | # default to 1k 54 | if [[ "${RUN_SIZE,,}" = "__run_size__" ]] 55 | then 56 | RUN_SIZE=1k 57 | fi 58 | 59 | datasets/fetch_anghabench.sh --clang ${LLVM_VERSION} --bitcode --run-size ${RUN_SIZE} 60 | 61 | for i in *.tar.xz 62 | do 63 | tar -xJf $i 64 | done 65 | 66 | # Run the benchmark 67 | tool_run_scripts/rellic.py \ 68 | --run-name "[${RUN_NAME}] [size: ${RUN_SIZE}] [rellic: ${RELLIC_BRANCH}]" \ 69 | --rellic rellic-decomp \ 70 | --input-dir $(pwd)/bitcode \ 71 | --output-dir $(pwd)/decompiled \ 72 | --slack-notify 73 | 74 | # Try to recompile our decompiled code 75 | tool_run_scripts/recompile.py \ 76 | --run-name "[${RUN_NAME}] [size: ${RUN_SIZE}] [recompile]" \ 77 | --clang clang-${LLVM_VERSION} \ 78 | --input-dir $(pwd)/decompiled \ 79 | --output-dir $(pwd)/recompiled \ 80 | --slack-notify 81 | 82 | # AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY passed in from original invocation environment 83 | if [[ "${AWS_ACCESS_KEY_ID,,}" != "" ]] 84 | then 85 | datenow=$(date +'%F-%H-%M') 86 | url_base="https://tob-amp-ci-results.nyc3.digitaloceanspaces.com" 87 | tar -Ipixz -cf rellic-ci-${datenow}.tar.xz decompiled 88 | tar -Ipixz -cf recompile-ci-${datenow}.tar.xz recompiled 89 | 90 | s3cmd -c /dev/null \ 91 | '--host-bucket=%(bucket)s.nyc3.digitaloceanspaces.com' \ 92 | --acl-public \ 93 | put \ 94 | rellic-ci-${datenow}.tar.xz \ 95 | s3://tob-amp-ci-results/rellic/ 96 | 97 | tool_run_scripts/slack.py \ 98 | --msg "Uploaded rellic decompilation results to ${url_base}/rellic/rellic-ci-${datenow}.tar.xz" 99 | 100 | 101 | s3cmd -c /dev/null \ 102 | '--host-bucket=%(bucket)s.nyc3.digitaloceanspaces.com' \ 103 | --acl-public \ 104 | put \ 105 | recompile-ci-${datenow}.tar.xz \ 106 | s3://tob-amp-ci-results/recompile/ 107 | 108 | tool_run_scripts/slack.py \ 109 | --msg "Uploaded recompilation results to ${url_base}/recompile/recompile-ci-${datenow}.tar.xz" 110 | fi 111 | 112 | # exit hook called here 113 | } &>> build.log 114 | 115 | exit 0 116 | -------------------------------------------------------------------------------- /scripts/test-angha-1k.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 3 | SRC_DIR=$( cd "$( dirname "${DIR}" )" && pwd ) 4 | 5 | LLVM_VERSION=14 6 | RELLIC_DECOMPILE="rellic-decomp-${LLVM_VERSION}.0" 7 | function Help 8 | { 9 | echo "Run Rellic on AnghaBech-1K" 10 | echo "" 11 | echo "Options:" 12 | echo " --rellic-cmd The rellic decompile command to invoke. Default ${RELLIC_DECOMPILE}" 13 | echo " -h --help Print help." 14 | } 15 | 16 | function check_test 17 | { 18 | local input_json=${1} 19 | if [[ ! -f ${1} ]] 20 | then 21 | echo "[!] Could not find python results for: ${input_json}" 22 | return 1 23 | fi 24 | 25 | # count number of failures 26 | fail_msg=$(\ 27 | PYTHONPATH=${SRC_DIR}/external/lifting-tools-ci/tool_run_scripts \ 28 | python3 -c "import stats,sys; s=stats.Stats(); s.load_json(sys.stdin); print(s.get_fail_count())" \ 29 | < ${input_json}) 30 | 31 | if [[ "${fail_msg}" != "0" ]] 32 | then 33 | echo "[!] There were [${fail_msg}] failures on ${arch}:" 34 | PYTHONPATH=${SRC_DIR}/external/lifting-tools-ci/tool_run_scripts \ 35 | python3 -c "import stats,sys; s=stats.Stats(); s.load_json(sys.stdin); s.print_fails()" \ 36 | < ${input_json} 37 | return 1 38 | fi 39 | 40 | return 0 41 | } 42 | 43 | set -euo pipefail 44 | 45 | while [[ $# -gt 0 ]] ; do 46 | key="$1" 47 | 48 | case $key in 49 | 50 | -h) 51 | Help 52 | exit 0 53 | ;; 54 | 55 | --help) 56 | Help 57 | exit 0 58 | ;; 59 | 60 | 61 | # Cmd to run for decompilation 62 | --rellic-cmd) 63 | RELLIC_DECOMPILE=${2} 64 | shift # past argument 65 | ;; 66 | 67 | *) 68 | # unknown option 69 | echo "[x] Unknown option: ${key}" 70 | exit 1 71 | ;; 72 | esac 73 | 74 | shift # past argument or value 75 | done 76 | 77 | 78 | if ! ${RELLIC_DECOMPILE} --version &>/dev/null; 79 | then 80 | echo "[!] Could not execute rellic cmd: ${RELLIC_DECOMPILE}" 81 | exit 1 82 | fi 83 | 84 | # create a working directory 85 | mkdir -p rellic-angha-test-1k 86 | pushd rellic-angha-test-1k 87 | 88 | # fetch the test set: 1K bitcode (per arch) 89 | ${SRC_DIR}/external/lifting-tools-ci/datasets/fetch_anghabench.sh --run-size 1k --bitcode --clang 14 90 | # extract it 91 | for tarfile in *.tar.xz 92 | do 93 | tar -xJf ${tarfile} 94 | done 95 | 96 | FAILED="no" 97 | for arch in $(ls -1 bitcode/) 98 | do 99 | echo "[+] Testing architecture ${arch}" 100 | ${SRC_DIR}/external/lifting-tools-ci/tool_run_scripts/rellic.py \ 101 | --rellic "${RELLIC_DECOMPILE}" \ 102 | --input-dir "$(pwd)/bitcode/${arch}" \ 103 | --output-dir "$(pwd)/decompile/${arch}" \ 104 | --run-name "rellic-live-ci-${arch}" \ 105 | --test-options "${SRC_DIR}/ci/angha_1k_test_settings.json" \ 106 | --dump-stats 107 | 108 | if ! check_test "$(pwd)/decompile/${arch}/stats.json" 109 | then 110 | echo "[!] Failed decompilation for ${arch}" 111 | FAILED="yes" 112 | fi 113 | 114 | # This is currently informational only 115 | mkdir -p "$(pwd)/recompile/${arch}" 116 | ${SRC_DIR}/external/lifting-tools-ci/tool_run_scripts/recompile.py \ 117 | --clang "clang-${LLVM_VERSION}" \ 118 | --input-dir "$(pwd)/decompile/${arch}" \ 119 | --output-dir "$(pwd)/recompile/${arch}" \ 120 | --run-name "recompile-live-ci-${arch}" \ 121 | --dump-stats 122 | done 123 | 124 | if [[ "${FAILED}" = "no" ]] 125 | then 126 | echo "[+] All tests successful!" 127 | exit 0 128 | fi 129 | 130 | echo "[!] One or more failures encountered during test" 131 | exit 1 132 | -------------------------------------------------------------------------------- /scripts/test-headergen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import unittest 4 | import subprocess 5 | import argparse 6 | import tempfile 7 | import os 8 | import sys 9 | 10 | 11 | class RunError(Exception): 12 | def __init__(self, msg): 13 | self.msg = msg 14 | 15 | def __str__(self): 16 | return str(self.msg) 17 | 18 | 19 | def run_cmd(cmd, timeout): 20 | try: 21 | p = subprocess.run( 22 | cmd, 23 | stdout=subprocess.PIPE, 24 | stderr=subprocess.PIPE, 25 | timeout=timeout, 26 | universal_newlines=True, 27 | ) 28 | except FileNotFoundError as e: 29 | raise RunError('Error: No such file or directory: "' + e.filename + '"') 30 | except PermissionError as e: 31 | raise RunError('Error: File "' + e.filename + '" is not an executable.') 32 | 33 | return p 34 | 35 | 36 | def compile(self, clang, input, output, timeout, options=None): 37 | cmd = [] 38 | cmd.append(clang) 39 | if options is not None: 40 | cmd.extend(options) 41 | cmd.extend([input, "-o", output]) 42 | p = run_cmd(cmd, timeout) 43 | 44 | self.assertEqual( 45 | len(p.stderr), 0, "errors or warnings during compilation: %s" % p.stderr 46 | ) 47 | self.assertEqual(p.returncode, 0, "clang failure") 48 | 49 | return p 50 | 51 | 52 | def genlayout(self, rellic, input, output, timeout): 53 | cmd = [rellic] 54 | cmd.extend( 55 | ["--input", input, "--output", output] 56 | ) 57 | p = run_cmd(cmd, timeout) 58 | 59 | self.assertEqual( 60 | len(p.stderr), 0, "errors or warnings during header generation: %s" % p.stderr 61 | ) 62 | self.assertEqual(p.returncode, 0, "rellic-headergen failure: %s" % p.stderr) 63 | 64 | return p 65 | 66 | 67 | def roundtrip(self, rellic, filename, clang, timeout, cflags): 68 | with tempfile.TemporaryDirectory() as tempdir: 69 | rt_bc = os.path.join(tempdir, "rt.bc") 70 | flags = ["-c", "-emit-llvm", "-g3"] 71 | compile(self, clang, filename, rt_bc, timeout, cflags + flags) 72 | 73 | rt_c = os.path.join(tempdir, "rt.c") 74 | genlayout(self, rellic, rt_bc, rt_c, timeout) 75 | 76 | # ensure there is a C output file 77 | self.assertTrue(os.path.exists(rt_c)) 78 | 79 | # ensure the file has some C 80 | self.assertTrue(os.path.getsize(rt_c) > 0) 81 | 82 | # We should recompile, lets see how this goes 83 | out2 = os.path.join(tempdir, "out2") 84 | compile(self, clang, rt_c, out2, timeout, cflags + ["-c", "-Wno-everything"]) 85 | 86 | 87 | class TestRoundtrip(unittest.TestCase): 88 | pass 89 | 90 | 91 | if __name__ == "__main__": 92 | parser = argparse.ArgumentParser() 93 | parser.add_argument("rellic", help="path to rellic-headergen") 94 | parser.add_argument("tests", help="path to test directory") 95 | parser.add_argument("clang", help="path to clang") 96 | parser.add_argument("-t", "--timeout", help="set timeout in seconds", type=int) 97 | parser.add_argument( 98 | "--cflags", help="additional CFLAGS", action='append', default=[], type=str) 99 | 100 | args = parser.parse_args() 101 | 102 | def test_generator(path): 103 | def test(self): 104 | roundtrip(self, args.rellic, path, args.clang, args.timeout, args.cflags) 105 | 106 | return test 107 | 108 | for item in os.scandir(args.tests): 109 | if item.is_file(): 110 | name, ext = os.path.splitext(item.name) 111 | # Allow for READMEs and data/headers 112 | if ext in [".c", ".cpp"]: 113 | test_name = f"test_{name}" 114 | test = test_generator(item.path) 115 | setattr(TestRoundtrip, test_name, test) 116 | 117 | unittest.main(argv=[sys.argv[0]]) 118 | -------------------------------------------------------------------------------- /tests/tools/decomp/array_swap.c: -------------------------------------------------------------------------------- 1 | int a[2] = {0, 42}; 2 | 3 | int main(void) { 4 | int b = a[0]; 5 | a[0] = a[1]; 6 | a[1] = b; 7 | return a[0]; 8 | } -------------------------------------------------------------------------------- /tests/tools/decomp/assert.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | unsigned long a = 1; 4 | 5 | int main(void) { 6 | assert(a % 3); 7 | assert(a % 7); 8 | assert(a % 15); 9 | 10 | return 0; 11 | } -------------------------------------------------------------------------------- /tests/tools/decomp/binops.c: -------------------------------------------------------------------------------- 1 | unsigned int target(unsigned int n) { 2 | unsigned int mod = n % 4; 3 | unsigned int result = 0; 4 | 5 | if (mod == 0) { 6 | result = (n | 0xbaaad0bf) * (2 ^ n); 7 | } else if (mod == 1) { 8 | result = (n & 0xbaaad0bf) * (3 + n); 9 | } else if (mod == 2) { 10 | result = (n ^ 0xbaaad0bf) * (4 | n); 11 | } else { 12 | result = (n + 0xbaaad0bf) * (5 & n); 13 | } 14 | 15 | return result; 16 | } 17 | 18 | int main(void) { return target(0xdeadbeef); } 19 | -------------------------------------------------------------------------------- /tests/tools/decomp/bitmask.c: -------------------------------------------------------------------------------- 1 | unsigned long a = 0xABCD; 2 | int main(void) { 3 | if (((a & 0x0FF0) >> 4) == 0xBC) { 4 | return 0; 5 | } else { 6 | return 1; 7 | } 8 | } -------------------------------------------------------------------------------- /tests/tools/decomp/bitops.c: -------------------------------------------------------------------------------- 1 | unsigned a = 0xFF; 2 | unsigned b = 7; 3 | 4 | int main(void) { 5 | int retval = 0; 6 | if (((int)a >> b) & 1) { 7 | retval += 1; 8 | } 9 | if ((a >> b) ^ 1) { 10 | retval += 2; 11 | } 12 | if ((a << b) || 1) { 13 | retval += 3; 14 | } 15 | return retval; 16 | } -------------------------------------------------------------------------------- /tests/tools/decomp/bool.c: -------------------------------------------------------------------------------- 1 | unsigned long a = 0; 2 | unsigned long b = 1; 3 | int main(void) { 4 | if ((a == b) == 0) { 5 | return 0; 6 | } else { 7 | return 1; 8 | } 9 | } -------------------------------------------------------------------------------- /tests/tools/decomp/branch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | unsigned a = 0; 5 | unsigned c = 1; 6 | 7 | int main(void) { 8 | uintptr_t b = (uintptr_t)&a; 9 | uintptr_t d = (uintptr_t)&c; 10 | 11 | if (c) { 12 | printf("Global variable 'a' of value %u is at ", a); 13 | if (b % 2 == 0) 14 | printf("even "); 15 | else 16 | printf("odd "); 17 | } else { 18 | printf("Global variable 'c' of value %u is at ", c); 19 | if (d % 2 == 0) 20 | printf("even "); 21 | else 22 | printf("odd "); 23 | } 24 | 25 | printf("address.\n"); 26 | 27 | return 0; 28 | } -------------------------------------------------------------------------------- /tests/tools/decomp/byval_struct.c: -------------------------------------------------------------------------------- 1 | extern int printf(const char* f, ...); 2 | extern int atoi(const char* s); 3 | 4 | struct foo { 5 | long long x, y, z, w; 6 | }; 7 | 8 | long long get_3x(struct foo f) { 9 | f.x = f.x * 3; 10 | return f.x; 11 | } 12 | 13 | int main() { 14 | struct foo f = {atoi("1"), atoi("2"), atoi("3"), atoi("4")}; 15 | long long x = get_3x(f); 16 | printf("%lld %lld\n", f.x, x); 17 | } -------------------------------------------------------------------------------- /tests/tools/decomp/byval_tail_gep.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'byval_tail_gep.cpp' 2 | source_filename = "byval_tail_gep.cpp" 3 | target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" 4 | target triple = "x86_64-unknown-linux-gnu" 5 | 6 | %struct.big_derived = type { %struct.big_base } 7 | %struct.big_base = type { [32 x i32] } 8 | 9 | ; Function Attrs: uwtable mustprogress 10 | define dso_local double @_Z3foo11big_derived(%struct.big_derived* nocapture readonly byval(%struct.big_derived) align 8 %x) local_unnamed_addr #0 !dbg !7 { 11 | entry: 12 | call void @llvm.dbg.declare(metadata %struct.big_derived* %x, metadata !23, metadata !DIExpression()), !dbg !24 13 | %tmpcast = getelementptr inbounds %struct.big_derived, %struct.big_derived* %x, i64 0, i32 0, !dbg !25 14 | %call = tail call double @_Z3bar8big_base(%struct.big_base* nonnull byval(%struct.big_base) align 8 %tmpcast), !dbg !26 15 | ret double %call, !dbg !27 16 | } 17 | 18 | ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn 19 | declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 20 | 21 | declare !dbg !28 dso_local double @_Z3bar8big_base(%struct.big_base* byval(%struct.big_base) align 8) local_unnamed_addr #2 22 | 23 | attributes #0 = { uwtable mustprogress "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } 24 | attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } 25 | attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } 26 | 27 | !llvm.dbg.cu = !{!0} 28 | !llvm.module.flags = !{!3, !4, !5} 29 | !llvm.ident = !{!6} 30 | 31 | !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.1 (https://github.com/microsoft/vcpkg.git 2a31089e777fc187f1cc05338250b8e1810cfb52)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) 32 | !1 = !DIFile(filename: "byval_tail_gep.cpp", directory: "/") 33 | !2 = !{} 34 | !3 = !{i32 7, !"Dwarf Version", i32 4} 35 | !4 = !{i32 2, !"Debug Info Version", i32 3} 36 | !5 = !{i32 1, !"wchar_size", i32 4} 37 | !6 = !{!"clang version 12.0.1 (https://github.com/microsoft/vcpkg.git 2a31089e777fc187f1cc05338250b8e1810cfb52)"} 38 | !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foo11big_derived", scope: !8, file: !8, line: 8, type: !9, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !22) 39 | !8 = !DIFile(filename: "byval_tail_gep.cpp", directory: "/") 40 | !9 = !DISubroutineType(types: !10) 41 | !10 = !{!11, !12} 42 | !11 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) 43 | !12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "big_derived", file: !8, line: 4, size: 1024, flags: DIFlagTypePassByValue, elements: !13, identifier: "_ZTS11big_derived") 44 | !13 = !{!14} 45 | !14 = !DIDerivedType(tag: DW_TAG_inheritance, scope: !12, baseType: !15, extraData: i32 0) 46 | !15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "big_base", file: !8, line: 1, size: 1024, flags: DIFlagTypePassByValue, elements: !16, identifier: "_ZTS8big_base") 47 | !16 = !{!17} 48 | !17 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !15, file: !8, line: 2, baseType: !18, size: 1024) 49 | !18 = !DICompositeType(tag: DW_TAG_array_type, baseType: !19, size: 1024, elements: !20) 50 | !19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) 51 | !20 = !{!21} 52 | !21 = !DISubrange(count: 32) 53 | !22 = !{!23} 54 | !23 = !DILocalVariable(name: "x", arg: 1, scope: !7, file: !8, line: 8, type: !12) 55 | !24 = !DILocation(line: 8, column: 31, scope: !7) 56 | !25 = !DILocation(line: 9, column: 14, scope: !7) 57 | !26 = !DILocation(line: 9, column: 10, scope: !7) 58 | !27 = !DILocation(line: 9, column: 3, scope: !7) 59 | !28 = !DISubprogram(name: "bar", linkageName: "_Z3bar8big_base", scope: !8, file: !8, line: 7, type: !29, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) 60 | !29 = !DISubroutineType(types: !30) 61 | !30 = !{!11, !15} 62 | -------------------------------------------------------------------------------- /tests/tools/decomp/byval_tail_nogep.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'byval_tail_nogep.cpp' 2 | source_filename = "byval_tail_nogep.cpp" 3 | target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" 4 | target triple = "x86_64-unknown-linux-gnu" 5 | 6 | %struct.big_base = type { [32 x i32] } 7 | 8 | ; Function Attrs: uwtable mustprogress 9 | define dso_local double @_Z3foo8big_base(%struct.big_base* nocapture readonly byval(%struct.big_base) align 8 %x) local_unnamed_addr #0 !dbg !7 { 10 | entry: 11 | call void @llvm.dbg.declare(metadata %struct.big_base* %x, metadata !20, metadata !DIExpression()), !dbg !21 12 | %call = tail call double @_Z3bar8big_base(%struct.big_base* nonnull byval(%struct.big_base) align 8 %x), !dbg !22 13 | ret double %call, !dbg !23 14 | } 15 | 16 | ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn 17 | declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 18 | 19 | declare !dbg !24 dso_local double @_Z3bar8big_base(%struct.big_base* byval(%struct.big_base) align 8) local_unnamed_addr #2 20 | 21 | attributes #0 = { uwtable mustprogress "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } 22 | attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } 23 | attributes #2 = { "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } 24 | 25 | !llvm.dbg.cu = !{!0} 26 | !llvm.module.flags = !{!3, !4, !5} 27 | !llvm.ident = !{!6} 28 | 29 | !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.1 (https://github.com/microsoft/vcpkg.git 2a31089e777fc187f1cc05338250b8e1810cfb52)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) 30 | !1 = !DIFile(filename: "byval_tail_nogep.cpp", directory: "/") 31 | !2 = !{} 32 | !3 = !{i32 7, !"Dwarf Version", i32 4} 33 | !4 = !{i32 2, !"Debug Info Version", i32 3} 34 | !5 = !{i32 1, !"wchar_size", i32 4} 35 | !6 = !{!"clang version 12.0.1 (https://github.com/microsoft/vcpkg.git 2a31089e777fc187f1cc05338250b8e1810cfb52)"} 36 | !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foo8big_base", scope: !8, file: !8, line: 5, type: !9, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !19) 37 | !8 = !DIFile(filename: "byval_tail_nogep.cpp", directory: "/") 38 | !9 = !DISubroutineType(types: !10) 39 | !10 = !{!11, !12} 40 | !11 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) 41 | !12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "big_base", file: !8, line: 1, size: 1024, flags: DIFlagTypePassByValue, elements: !13, identifier: "_ZTS8big_base") 42 | !13 = !{!14} 43 | !14 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !12, file: !8, line: 2, baseType: !15, size: 1024) 44 | !15 = !DICompositeType(tag: DW_TAG_array_type, baseType: !16, size: 1024, elements: !17) 45 | !16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) 46 | !17 = !{!18} 47 | !18 = !DISubrange(count: 32) 48 | !19 = !{!20} 49 | !20 = !DILocalVariable(name: "x", arg: 1, scope: !7, file: !8, line: 5, type: !12) 50 | !21 = !DILocation(line: 5, column: 28, scope: !7) 51 | !22 = !DILocation(line: 6, column: 10, scope: !7) 52 | !23 = !DILocation(line: 6, column: 3, scope: !7) 53 | !24 = !DISubprogram(name: "bar", linkageName: "_Z3bar8big_base", scope: !8, file: !8, line: 4, type: !9, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) 54 | -------------------------------------------------------------------------------- /tests/tools/decomp/cast.c: -------------------------------------------------------------------------------- 1 | int a = 0; 2 | int main(void) { return (long long)&a; } -------------------------------------------------------------------------------- /tests/tools/decomp/conflicting_global.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int a = 3; 4 | 5 | int main(void) { 6 | { 7 | int a = 4; 8 | printf("%d\n", a); 9 | } 10 | printf("%d\n", a); 11 | } 12 | -------------------------------------------------------------------------------- /tests/tools/decomp/conflicting_names.c: -------------------------------------------------------------------------------- 1 | extern int atoi(const char *); 2 | 3 | int foo() { 4 | int a = 0; 5 | return a; 6 | } 7 | 8 | int bar() { 9 | int a = 1; 10 | return a; 11 | } 12 | 13 | int main() { 14 | int argc = 0; 15 | char **argv = 0; 16 | int ret = 0; 17 | if (1 < argc) { 18 | int argc = atoi(argv[1]); 19 | if (argc > 10) { 20 | int argc = 99; 21 | ret = argc; 22 | } 23 | } else { 24 | int argv = 1; 25 | ret = argv; 26 | } 27 | return ret; 28 | } -------------------------------------------------------------------------------- /tests/tools/decomp/diff_outputs.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | SHELL = /usr/bin/env bash 10 | 11 | CLANG_FLAGS = -c -emit-llvm 12 | C_DIFFS := $(patsubst %.c,%.diff,$(wildcard *.c)) 13 | CPP_DIFFS := $(patsubst %.cpp,%.diff,$(wildcard *.cpp)) 14 | LL_DIFFS := $(patsubst %.ll,%.diff,$(wildcard *.ll)) 15 | 16 | .PHONY: all clean 17 | 18 | %.bc : %.c $(OLD_RELLIC) $(NEW_RELLIC) 19 | @$(CLANG) $(CLANG_FLAGS) -o $@ $< > /dev/null 20 | 21 | %.bc : %.cpp $(OLD_RELLIC) $(NEW_RELLIC) 22 | @$(CLANG) $(CLANG_FLAGS) -o $@ $< > /dev/null 23 | 24 | %.diff : %.bc 25 | @echo "### $<" 26 | @echo "" 27 | @echo "\`\`\`diff" 28 | -@diff -u \ 29 | <( $(OLD_RELLIC) --input $< --output /dev/stdout ) \ 30 | <( $(NEW_RELLIC) --input $< --output /dev/stdout ) || true 31 | @echo "\`\`\`" 32 | @echo "" 33 | 34 | %.diff : %.ll 35 | @echo "### $<" 36 | @echo "" 37 | @echo "\`\`\`diff" 38 | -@diff -u \ 39 | <( $(OLD_RELLIC) --input $< --output /dev/stdout ) \ 40 | <( $(NEW_RELLIC) --input $< --output /dev/stdout ) || true 41 | @echo "\`\`\`" 42 | @echo "" 43 | 44 | all: $(C_DIFFS) $(CPP_DIFFS) $(LL_DIFFS) 45 | 46 | clean: 47 | -rm *.bc -------------------------------------------------------------------------------- /tests/tools/decomp/failing-rebuild/README.md: -------------------------------------------------------------------------------- 1 | # Tests Failing Rebuild 2 | 3 | The tests here should translate to C from bitcode, but the resulting C may not necessarily reubuild and function equivalent to the original C. -------------------------------------------------------------------------------- /tests/tools/decomp/failing-rebuild/chal-1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define serial_print(...) \ 6 | do { \ 7 | printf(__VA_ARGS__); \ 8 | } while (0) 9 | #define serial_println(...) \ 10 | do { \ 11 | printf(__VA_ARGS__); \ 12 | printf("\n"); \ 13 | } while (0) 14 | bool brake_state = false; 15 | bool need_to_flash = false; 16 | bool previous_brake_state = false; 17 | void brake_on() { serial_println("%s called", __FUNCTION__); } 18 | void brake_off() { serial_println("%s called", __FUNCTION__); } 19 | void rx_message_routine(unsigned char buf[]) { 20 | int16_t speed_value = (((int16_t)buf[3]) << 8) + buf[2]; 21 | uint8_t brake_switch = (buf[4] & 0b00001100) >> 2; 22 | serial_print("%s", " Speed = "); 23 | serial_print("%d", speed_value / 256); 24 | serial_print("%s", ", brake ="); 25 | serial_print("%d", brake_switch); 26 | serial_println("%s", "]"); 27 | if (brake_switch) { 28 | brake_state = true; 29 | brake_on(); 30 | if (speed_value > 0 && 31 | previous_brake_state != 32 | brake_state) { // speed > 0 and brakes were off last 33 | need_to_flash = true; 34 | serial_println("%s", "Flashing=true"); 35 | } 36 | } else { 37 | brake_state = false; 38 | need_to_flash = false; 39 | brake_off(); 40 | } 41 | previous_brake_state = brake_state; 42 | } 43 | int main(int argc, const char *argv[]) { 44 | // default input 45 | unsigned char buf[8] = {0, 1, 2, 3, 4, 5, 6, 7}; 46 | // optional input 47 | for (int i = 1; i < argc && i < sizeof(buf) / sizeof(buf[0]); i++) { 48 | buf[i] = (unsigned char)atoi(argv[i]); 49 | } 50 | rx_message_routine(buf); 51 | return 0; 52 | } -------------------------------------------------------------------------------- /tests/tools/decomp/failing-rebuild/issue_333_inline_asm.c: -------------------------------------------------------------------------------- 1 | int main(int argc, char *argv[]) { __asm__(""); } 2 | -------------------------------------------------------------------------------- /tests/tools/decomp/fcmp.c: -------------------------------------------------------------------------------- 1 | float atof(const char*); 2 | int printf(const char*, ...); 3 | 4 | int main() { 5 | float NaN = 0.0f / 0.0f; 6 | float x = atof("3"); 7 | float y = atof("2"); 8 | float z = -x; 9 | 10 | int a = __builtin_isgreater(z, NaN); 11 | int b = __builtin_isgreaterequal(z, NaN); 12 | 13 | int c = __builtin_isgreater(x, y); 14 | int d = __builtin_isgreaterequal(x, y); 15 | 16 | int e = __builtin_isunordered(x, NaN); 17 | int f = __builtin_isunordered(x, y); 18 | 19 | printf("%d %d %d %d %d %d\n", a, b, c, d, e, f); 20 | } -------------------------------------------------------------------------------- /tests/tools/decomp/fizzbuzz.c: -------------------------------------------------------------------------------- 1 | int printf(const char*, ...); 2 | 3 | int main() { 4 | for(int i = 0; i < 30; ++i) { 5 | if(i % 3 == 0 && i % 5 == 0) { 6 | printf("fizzbuzz\n"); 7 | } else if(i % 3 == 0) { 8 | printf("fizz\n"); 9 | } else if(i % 5 == 0) { 10 | printf("buzz\n"); 11 | } else { 12 | printf("%d\n", i); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/tools/decomp/fizzbuzz_stateful.c: -------------------------------------------------------------------------------- 1 | int printf(const char*, ...); 2 | 3 | int i; 4 | 5 | void fizzbuzz() { 6 | int save = i; 7 | if (i % 3 == 0) { 8 | i = 4; 9 | printf("fizz"); 10 | } 11 | 12 | if (i % 5 == 0) { 13 | printf("buzz"); 14 | } 15 | 16 | if ((i % 3 != 0) && (i % 5 != 0)) { 17 | printf("%d", i); 18 | } 19 | i = save; 20 | } 21 | 22 | int main() { 23 | for (i = 1; i < 16; i++) { 24 | fizzbuzz(); 25 | printf("\n"); 26 | } 27 | } -------------------------------------------------------------------------------- /tests/tools/decomp/float.c: -------------------------------------------------------------------------------- 1 | float f = 3.14; 2 | int main(void) { 3 | double d = 22.0 / 7.0; 4 | double e = d - f; 5 | if (e < 0.01) { 6 | return 0; 7 | } else { 8 | return 1; 9 | } 10 | } -------------------------------------------------------------------------------- /tests/tools/decomp/func_cond_two_arg.c: -------------------------------------------------------------------------------- 1 | unsigned f(int a, int b) { return a < b; } 2 | int x = 0; 3 | int y = 1; 4 | int main(void) { 5 | if (f(x, y)) { 6 | return 1; 7 | } 8 | return 0; 9 | } -------------------------------------------------------------------------------- /tests/tools/decomp/func_cond_zero_arg.c: -------------------------------------------------------------------------------- 1 | unsigned f() { return 1U; } 2 | int x = 0; 3 | int y = 1; 4 | int main(void) { 5 | if (f()) { 6 | return 1; 7 | } 8 | return 0; 9 | } -------------------------------------------------------------------------------- /tests/tools/decomp/funcptr.c: -------------------------------------------------------------------------------- 1 | int add(int a, int b) { return a + b; } 2 | 3 | int sub(int a, int b) { return a - b; } 4 | 5 | int x = 0; 6 | 7 | int main(void) { 8 | int (*func)(int, int); 9 | if (x) { 10 | func = add; 11 | } else { 12 | func = sub; 13 | } 14 | return func(2, 2); 15 | } -------------------------------------------------------------------------------- /tests/tools/decomp/global_using_function_decl.c: -------------------------------------------------------------------------------- 1 | void some_func(int arg); 2 | 3 | void (*afunc_pointer)(int) = &some_func; 4 | 5 | void some_func(int arg) {} 6 | 7 | int main(void) { afunc_pointer(0); } -------------------------------------------------------------------------------- /tests/tools/decomp/goto_loop.c: -------------------------------------------------------------------------------- 1 | int printf(const char*, ...); 2 | 3 | int main() { 4 | int i = 0; 5 | start: 6 | i++; 7 | if(i == 1) { 8 | printf("%d\n", i); 9 | goto start; 10 | } else if(i == 2) { 11 | printf("%d\n", i); 12 | goto start; 13 | } else if(i == 3) { 14 | printf("%d\n", i);; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/tools/decomp/init_list.c: -------------------------------------------------------------------------------- 1 | unsigned a[5] = {0, 1, 2, 3, 4}; 2 | 3 | int main(void) { return a[1]; } -------------------------------------------------------------------------------- /tests/tools/decomp/inttoptr.c: -------------------------------------------------------------------------------- 1 | #include 2 | unsigned long a = 0xDEADBEEF; 3 | int main(void) { 4 | int *b = (int *)a; 5 | if (b != NULL) { 6 | return 42; 7 | } else { 8 | return 0; 9 | } 10 | } -------------------------------------------------------------------------------- /tests/tools/decomp/issue_123_uint128_t.c: -------------------------------------------------------------------------------- 1 | #if defined(__x86_64__) || defined(__i386__) || defined(_M_X86) || \ 2 | defined(__arm__) 3 | typedef unsigned uint128_t __attribute__((mode(TI))); 4 | typedef int int128_t __attribute__((mode(TI))); 5 | #elif defined(__aarch64__) 6 | typedef __uint128_t uint128_t; 7 | typedef __int128_t int128_t; 8 | #elif defined(__sparc__) 9 | typedef __uint128_t uint128_t; 10 | typedef __int128_t int128_t; 11 | #elif !__is_identifier(_ExtInt) 12 | typedef unsigned _ExtInt(128) uint128_t; 13 | typedef signed _ExtInt(128) int128_t; 14 | #else 15 | #error "Unable to identify u/int128 type." 16 | #endif 17 | 18 | unsigned long long x = 0xDEADBEEF; 19 | 20 | int main(void) { 21 | uint128_t a = x; 22 | x = a; 23 | return x & 0xFF; 24 | } -------------------------------------------------------------------------------- /tests/tools/decomp/issue_127_uint128_t_lit.c: -------------------------------------------------------------------------------- 1 | #if defined(__x86_64__) || defined(__i386__) || defined(_M_X86) || \ 2 | defined(__arm__) 3 | typedef unsigned uint128_t __attribute__((mode(TI))); 4 | typedef int int128_t __attribute__((mode(TI))); 5 | #elif defined(__aarch64__) 6 | typedef __uint128_t uint128_t; 7 | typedef __int128_t int128_t; 8 | #elif defined(__sparc__) 9 | typedef __uint128_t uint128_t; 10 | typedef __int128_t int128_t; 11 | #elif !__is_identifier(_ExtInt) 12 | typedef unsigned _ExtInt(128) uint128_t; 13 | typedef signed _ExtInt(128) int128_t; 14 | #else 15 | #error "Unable to identify u/int128 type." 16 | #endif 17 | 18 | uint128_t x = 0xDEADBEEF; 19 | 20 | int main(void) { return ((x & 0xFFFF) == 0xBEEF) ? 0 : 1; } -------------------------------------------------------------------------------- /tests/tools/decomp/issue_183_literal_structs.c: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | int a; 3 | int b; 4 | union { 5 | int x; 6 | char y; 7 | }; 8 | union { 9 | int s; 10 | int t; 11 | }; 12 | } foo_t; 13 | 14 | int bar(foo_t arg) { 15 | return arg.s; 16 | } 17 | 18 | extern int printf(const char* f, ...); 19 | extern int atoi(const char* s); 20 | 21 | int main() { 22 | foo_t f; 23 | f.t = 3; 24 | printf("%d\n", bar(f)); 25 | } -------------------------------------------------------------------------------- /tests/tools/decomp/issue_335_z3_ite.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = '/sn640/NotDec/test/backend/../wasm/sysy/functional/32_while_if_test2.c' 2 | source_filename = "/sn640/NotDec/test/backend/../wasm/sysy/functional/32_while_if_test2.c" 3 | target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" 4 | target triple = "x86_64-pc-linux-gnu" 5 | 6 | ; Function Attrs: noinline nounwind uwtable 7 | define dso_local i32 @ifWhile() #0 { 8 | %1 = alloca i32, align 4 9 | %2 = alloca i32, align 4 10 | store i32 0, i32* %1, align 4 11 | store i32 3, i32* %2, align 4 12 | %3 = load i32, i32* %1, align 4 13 | %4 = icmp eq i32 %3, 5 14 | br i1 %4, label %5, label %15 15 | 16 | 5: ; preds = %0 17 | br label %6 18 | 19 | 6: ; preds = %9, %5 20 | %7 = load i32, i32* %2, align 4 21 | %8 = icmp eq i32 %7, 2 22 | br i1 %8, label %9, label %12 23 | 24 | 9: ; preds = %6 25 | %10 = load i32, i32* %2, align 4 26 | %11 = add nsw i32 %10, 2 27 | store i32 %11, i32* %2, align 4 28 | br label %6, !llvm.loop !6 29 | 30 | 12: ; preds = %6 31 | %13 = load i32, i32* %2, align 4 32 | %14 = add nsw i32 %13, 25 33 | store i32 %14, i32* %2, align 4 34 | br label %25 35 | 36 | 15: ; preds = %0 37 | br label %16 38 | 39 | 16: ; preds = %19, %15 40 | %17 = load i32, i32* %1, align 4 41 | %18 = icmp slt i32 %17, 5 42 | br i1 %18, label %19, label %24 43 | 44 | 19: ; preds = %16 45 | %20 = load i32, i32* %2, align 4 46 | %21 = mul nsw i32 %20, 2 47 | store i32 %21, i32* %2, align 4 48 | %22 = load i32, i32* %1, align 4 49 | %23 = add nsw i32 %22, 1 50 | store i32 %23, i32* %1, align 4 51 | br label %16, !llvm.loop !8 52 | 53 | 24: ; preds = %16 54 | br label %25 55 | 56 | 25: ; preds = %24, %12 57 | %26 = load i32, i32* %2, align 4 58 | ret i32 %26 59 | } 60 | 61 | ; Function Attrs: noinline nounwind uwtable 62 | define dso_local i32 @main() #0 { 63 | %1 = alloca i32, align 4 64 | store i32 0, i32* %1, align 4 65 | %2 = call i32 @ifWhile() 66 | ret i32 %2 67 | } 68 | 69 | attributes #0 = { noinline nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } 70 | 71 | !llvm.module.flags = !{!0, !1, !2, !3, !4} 72 | !llvm.ident = !{!5} 73 | 74 | !0 = !{i32 1, !"wchar_size", i32 4} 75 | !1 = !{i32 7, !"PIC Level", i32 2} 76 | !2 = !{i32 7, !"PIE Level", i32 2} 77 | !3 = !{i32 7, !"uwtable", i32 1} 78 | !4 = !{i32 7, !"frame-pointer", i32 2} 79 | !5 = !{!"Ubuntu clang version 14.0.0-1ubuntu1.1"} 80 | !6 = distinct !{!6, !7} 81 | !7 = !{!"llvm.loop.mustprogress"} 82 | !8 = distinct !{!8, !7} 83 | -------------------------------------------------------------------------------- /tests/tools/decomp/issue_4.c: -------------------------------------------------------------------------------- 1 | unsigned int foo(unsigned int a, unsigned int b) { 2 | unsigned int sum = 0; 3 | for (unsigned int i = 0; i != 42; i++) { 4 | sum += a; 5 | sum %= b; 6 | } 7 | return sum; 8 | } 9 | 10 | int main() { return foo(1, 200); } -------------------------------------------------------------------------------- /tests/tools/decomp/issue_94_strncmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | char str1[20]; 6 | char str2[20]; 7 | 8 | // Assigning the value to the string str1 9 | strcpy(str1, "hello"); 10 | 11 | // Assigning the value to the string str2 12 | strcpy(str2, "helLO WORLD"); 13 | 14 | // This will compare the first 3 characters 15 | if (strncmp(str1, str2, 3) > 0) { 16 | printf( 17 | "ASCII value of first unmatched character of str1 is greater than " 18 | "str2"); 19 | } else if (strncmp(str1, str2, 3) < 0) { 20 | printf( 21 | "ASCII value of first unmatched character of str1 is less than str2"); 22 | } else { 23 | printf("Both the strings str1 and str2 are equal"); 24 | } 25 | 26 | return 0; 27 | } -------------------------------------------------------------------------------- /tests/tools/decomp/known-failures/issue_126_bool2bv.c: -------------------------------------------------------------------------------- 1 | unsigned long a = 0xABCD; 2 | int main(void) { 3 | if (!((a & 0xF000) ^ 0xA000) & !((a & 0x0F00) ^ 0x0B00)) { 4 | return 0; 5 | } else { 6 | return 1; 7 | } 8 | } -------------------------------------------------------------------------------- /tests/tools/decomp/loop.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | for (unsigned b = 0; b != 10; ++b) { 5 | printf("Variable at %d is ", b); 6 | if (b % 2 == 0) 7 | printf("even.\n"); 8 | else 9 | printf("odd.\n"); 10 | } 11 | 12 | return 0; 13 | } -------------------------------------------------------------------------------- /tests/tools/decomp/nested_struct.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct _pair { 4 | int first; 5 | int second; 6 | }; 7 | 8 | struct _person { 9 | const char *name; 10 | char age; 11 | }; 12 | 13 | struct _record { 14 | int a; 15 | struct _pair b; 16 | struct _person c; 17 | }; 18 | 19 | struct _record r1 = {14, {33, 42}, {"Bob", 66}}; 20 | 21 | int main(void) { 22 | printf("Name: %s", r1.c.name); 23 | return r1.b.second; 24 | } -------------------------------------------------------------------------------- /tests/tools/decomp/nested_while.c: -------------------------------------------------------------------------------- 1 | int atoi(const char*); 2 | int printf(const char*, ...); 3 | 4 | int main() { 5 | int x = atoi("5"); 6 | if(x > 10) { 7 | while(x < 20) { 8 | x = x + 1; 9 | printf("loop1 x: %d\n", x); 10 | } 11 | } 12 | while(x < 20) { 13 | x = x + 1; 14 | printf("loop2 x: %d\n", x); 15 | } 16 | } -------------------------------------------------------------------------------- /tests/tools/decomp/nullptr.c: -------------------------------------------------------------------------------- 1 | int *ptr = ((void *)0); 2 | 3 | int main(void) { 4 | if (ptr == ((void *)0)) { 5 | return 0; 6 | } else { 7 | return 1; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/tools/decomp/reg_test_structure_fields.c: -------------------------------------------------------------------------------- 1 | struct a { 2 | int x; 3 | }; 4 | 5 | struct b { 6 | struct a subfield; 7 | }; 8 | 9 | int main(void) { 10 | struct b a; 11 | } 12 | -------------------------------------------------------------------------------- /tests/tools/decomp/ret0.c: -------------------------------------------------------------------------------- 1 | int f(void) { return 1; } 2 | int main(void) { return f(); } -------------------------------------------------------------------------------- /tests/tools/decomp/short.c: -------------------------------------------------------------------------------- 1 | unsigned short a; 2 | int main(void) { 3 | unsigned short b; 4 | a = 13; 5 | b = a; 6 | return b; 7 | } -------------------------------------------------------------------------------- /tests/tools/decomp/struct.c: -------------------------------------------------------------------------------- 1 | struct _pair { 2 | int first; 3 | int second; 4 | }; 5 | 6 | struct _pair a = {0, 42}; 7 | 8 | int main(void) { 9 | if (a.first) { 10 | return a.first; 11 | } else { 12 | return a.second; 13 | } 14 | } -------------------------------------------------------------------------------- /tests/tools/decomp/struct_swap.c: -------------------------------------------------------------------------------- 1 | struct _pair { 2 | int first; 3 | int second; 4 | }; 5 | 6 | struct _pair a = {0, 42}; 7 | 8 | int main(void) { 9 | int b = a.first; 10 | a.first = a.second; 11 | a.second = b; 12 | return a.first; 13 | } -------------------------------------------------------------------------------- /tests/tools/decomp/switch.c: -------------------------------------------------------------------------------- 1 | unsigned a = 12; 2 | int main(void) { 3 | int b = 0; 4 | switch (a) { 5 | case 1: 6 | b = 1; 7 | break; 8 | 9 | case 12: 10 | b = 21; 11 | break; 12 | 13 | case 123: 14 | b = 321; 15 | break; 16 | 17 | default: 18 | b = 255; 19 | break; 20 | } 21 | return b; 22 | } -------------------------------------------------------------------------------- /tests/tools/decomp/switch_loop.c: -------------------------------------------------------------------------------- 1 | int printf(const char*, ...); 2 | 3 | int main() { 4 | int i = 0; 5 | start: 6 | i++; 7 | switch(i) { 8 | case 1: printf("%d\n", i); goto start; break; 9 | case 2: printf("%d\n", i); goto start; break; 10 | case 3: printf("%d\n", i); break; 11 | } 12 | } -------------------------------------------------------------------------------- /tests/tools/decomp/template_parameter_pack.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | template 4 | T sum(T x, T y) { 5 | return x + y; 6 | } 7 | 8 | template 9 | T sum(T x, Ts... y) { 10 | return x + sum(y...); 11 | } 12 | 13 | int main(void) { printf("%d\n", sum(1, 2, 3, 4, 5)); } -------------------------------------------------------------------------------- /tests/tools/decomp/trunc.c: -------------------------------------------------------------------------------- 1 | unsigned long long a = -1; 2 | int main(void) { return a; } -------------------------------------------------------------------------------- /tests/tools/decomp/typedefs_of_typedefs.c: -------------------------------------------------------------------------------- 1 | #include 2 | typedef long long int mytype; 3 | typedef mytype foo; 4 | typedef foo bar; 5 | typedef bar baz __attribute__((vector_size(16))); 6 | typedef baz foobar; 7 | 8 | static foo array[4] = {}; 9 | 10 | int main() { 11 | foobar a, b, c; 12 | a = (foobar){1, 2}; 13 | b = (foobar){}; 14 | c = (foobar){4, 3}; 15 | a = a * b + c; 16 | int *pA = (int *)&a; 17 | 18 | printf("a=[%d %d]\n", pA[0], pA[1]); 19 | printf("array=[%lld %lld %lld %lld]\n", array[0], array[1], array[2], array[3]); 20 | return 0; 21 | } -------------------------------------------------------------------------------- /tests/tools/decomp/vectors.c: -------------------------------------------------------------------------------- 1 | #include 2 | typedef int v4si __attribute__((vector_size(16))); 3 | 4 | int main() { 5 | v4si a, b, c; 6 | a = (v4si){1, 2, 3, 4}; 7 | b = (v4si){}; 8 | c = (v4si){4, 3, 2, 1}; 9 | a = a * b + c; 10 | int *pA = (int *)&a; 11 | printf("a=[%d %d %d %d]\n", pA[0], pA[1], pA[2], pA[3]); 12 | return 0; 13 | } -------------------------------------------------------------------------------- /tests/tools/decomp/zeroinit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct _pair { 4 | int first; 5 | int second; 6 | }; 7 | 8 | struct _person { 9 | const char *name; 10 | char age; 11 | }; 12 | 13 | struct _record { 14 | int a; 15 | struct _pair b; 16 | struct _person c; 17 | }; 18 | 19 | struct _record r1 = {}; 20 | long long a1[256] = {}; 21 | 22 | int main(void) { 23 | if (r1.b.first != 0) return 1; 24 | 25 | if (a1[42] != 0) return 2; 26 | 27 | return 0; 28 | } -------------------------------------------------------------------------------- /tests/tools/decomp/zext.c: -------------------------------------------------------------------------------- 1 | unsigned char a = 1; 2 | int main(void) { return (unsigned int)a; } -------------------------------------------------------------------------------- /tests/tools/headergen/alias.cpp: -------------------------------------------------------------------------------- 1 | struct A { 2 | A* a; 3 | }; 4 | 5 | struct C { 6 | using foo = A; 7 | }; 8 | 9 | struct B { 10 | using foo = C::foo; 11 | foo a; 12 | }; 13 | 14 | void test(B b) {} -------------------------------------------------------------------------------- /tests/tools/headergen/alignment.c: -------------------------------------------------------------------------------- 1 | struct aligned { 2 | char c; 3 | short s; 4 | int i; 5 | long int l; 6 | }; 7 | 8 | void test(struct aligned arg) { } -------------------------------------------------------------------------------- /tests/tools/headergen/anon_union.c: -------------------------------------------------------------------------------- 1 | struct foo { 2 | union { 3 | char c; 4 | }; 5 | }; 6 | 7 | void test(struct foo f) {} -------------------------------------------------------------------------------- /tests/tools/headergen/atomic.c: -------------------------------------------------------------------------------- 1 | struct A { 2 | int _Atomic a; 3 | }; 4 | 5 | void test(struct A x) {} -------------------------------------------------------------------------------- /tests/tools/headergen/bigstruct.cpp: -------------------------------------------------------------------------------- 1 | struct big_foo_t { 2 | int x[1ull << 32]; 3 | }; 4 | 5 | void test(big_foo_t o) {} -------------------------------------------------------------------------------- /tests/tools/headergen/bitfield.c: -------------------------------------------------------------------------------- 1 | struct bitfield { 2 | int a : 1; 3 | int b : 1; 4 | int c : 1; 5 | }; 6 | 7 | void test(struct bitfield arg) { } -------------------------------------------------------------------------------- /tests/tools/headergen/enum_basetype.cpp: -------------------------------------------------------------------------------- 1 | enum myenum : unsigned long long { MYENUM_VAL = 1ull << 56 }; 2 | enum myenum2 { MYENUM_VAL2 = 1 }; 3 | 4 | void test(myenum e, myenum2 f) {} -------------------------------------------------------------------------------- /tests/tools/headergen/fwddecl.cpp: -------------------------------------------------------------------------------- 1 | struct foo; 2 | struct bar; 3 | struct baz; 4 | 5 | struct foo { 6 | bar* x; 7 | }; 8 | 9 | struct bar { 10 | foo* x; 11 | bar* y; 12 | baz* z; 13 | }; 14 | 15 | struct baz : foo { 16 | foo* x; 17 | }; 18 | 19 | void test(foo a, bar b, baz c) {} -------------------------------------------------------------------------------- /tests/tools/headergen/inheritance.cpp: -------------------------------------------------------------------------------- 1 | class A { 2 | public: 3 | int valA; 4 | char a; 5 | }; 6 | class B : public A { 7 | public: 8 | char b; 9 | }; 10 | class C : public B { 11 | public: 12 | char c; 13 | }; 14 | 15 | void testA(A* a) {} 16 | void testB(B* b) {} 17 | void testC(C* c) {} -------------------------------------------------------------------------------- /tests/tools/headergen/multiple_bases.cpp: -------------------------------------------------------------------------------- 1 | struct A1 { 2 | int a; 3 | }; 4 | 5 | struct A2 {}; 6 | 7 | struct B : public A1, public A2 {}; 8 | 9 | void test(B b) {} -------------------------------------------------------------------------------- /tests/tools/headergen/packed.c: -------------------------------------------------------------------------------- 1 | struct __attribute__((packed)) packed { 2 | char c; 3 | short s; 4 | int i; 5 | long int l; 6 | }; 7 | 8 | void test(struct packed arg) { } -------------------------------------------------------------------------------- /tests/tools/headergen/packed_bitfield.c: -------------------------------------------------------------------------------- 1 | struct __attribute__((packed)) b1 { 2 | int a:12; 3 | int b:32; 4 | int c:4; 5 | }; 6 | 7 | void test(struct b1 arg) {} -------------------------------------------------------------------------------- /tests/tools/headergen/packed_bitfield_mix.c: -------------------------------------------------------------------------------- 1 | struct __attribute__((packed)) b1 { 2 | int a:12; 3 | int b; 4 | int c:4; 5 | }; 6 | 7 | void test(struct b1 arg) {} -------------------------------------------------------------------------------- /tests/tools/headergen/static_member.cpp: -------------------------------------------------------------------------------- 1 | struct static_members_t { 2 | int a; 3 | static int b; 4 | }; 5 | 6 | int static_members_t::b = 0; 7 | 8 | void test(static_members_t arg) {} -------------------------------------------------------------------------------- /tests/tools/headergen/union.c: -------------------------------------------------------------------------------- 1 | union union_t { 2 | char c; 3 | short s; 4 | int i; 5 | long int l; 6 | }; 7 | 8 | void test(union union_t arg) { } -------------------------------------------------------------------------------- /tests/tools/headergen/unordered_map.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void test(std::unordered_map f) {} -------------------------------------------------------------------------------- /tests/tools/headergen/vector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char* argv[]) { 4 | std::vector args(argc); 5 | return args.size(); 6 | } -------------------------------------------------------------------------------- /tests/tools/headergen/vector_string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct foo { 5 | std::vector v; 6 | bool b; 7 | }; 8 | 9 | struct bar { 10 | foo f; 11 | int i; 12 | }; 13 | 14 | void test(bar b) {} -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020-present, Trail of Bits, Inc. 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | # 10 | # rellic-decomp 11 | # 12 | 13 | set(RELLIC_DECOMP "${PROJECT_NAME}-decomp") 14 | 15 | add_executable(${RELLIC_DECOMP} 16 | "decomp/Decomp.cpp" 17 | ) 18 | 19 | target_link_libraries(${RELLIC_DECOMP} 20 | PRIVATE 21 | "${PROJECT_NAME}_cxx_settings" 22 | "${PROJECT_NAME}" 23 | gflags::gflags 24 | ) 25 | 26 | set(RELLIC_DECOMP "${RELLIC_DECOMP}" PARENT_SCOPE) 27 | 28 | # 29 | # rellic-headergen 30 | # 31 | 32 | set(RELLIC_HEADERGEN "${PROJECT_NAME}-headergen") 33 | 34 | add_executable(${RELLIC_HEADERGEN} 35 | "headergen/HeaderGen.cpp" 36 | ) 37 | 38 | target_link_libraries(${RELLIC_HEADERGEN} 39 | PRIVATE 40 | "${PROJECT_NAME}_cxx_settings" 41 | "${PROJECT_NAME}" 42 | gflags::gflags 43 | ) 44 | 45 | set(RELLIC_HEADERGEN "${RELLIC_HEADERGEN}" PARENT_SCOPE) 46 | 47 | # 48 | # rellic-xref 49 | # 50 | 51 | set(RELLIC_XREF "${PROJECT_NAME}-xref") 52 | 53 | add_executable(${RELLIC_XREF} 54 | "xref/DeclPrinter.cpp" 55 | "xref/StmtPrinter.cpp" 56 | "xref/TypePrinter.cpp" 57 | "xref/Xref.cpp" 58 | ) 59 | 60 | find_path(CPP_HTTPLIB_INCLUDE_DIRS "httplib.h") 61 | target_include_directories(${RELLIC_XREF} PRIVATE ${CPP_HTTPLIB_INCLUDE_DIRS}) 62 | 63 | target_link_libraries(${RELLIC_XREF} 64 | PRIVATE 65 | "${PROJECT_NAME}_cxx_settings" 66 | "${PROJECT_NAME}" 67 | gflags::gflags 68 | ) 69 | 70 | set(RELLIC_XREF "${RELLIC_XREF}" PARENT_SCOPE) 71 | 72 | # 73 | # rellic-repl 74 | # 75 | 76 | include(FetchContent) 77 | FetchContent_Declare(linenoise 78 | GIT_REPOSITORY https://github.com/antirez/linenoise.git 79 | GIT_TAG 97d2850af13c339369093b78abe5265845d78220 80 | ) 81 | FetchContent_MakeAvailable(linenoise) 82 | 83 | add_library(linenoise STATIC 84 | "${linenoise_SOURCE_DIR}/linenoise.c" 85 | ) 86 | target_include_directories(linenoise 87 | PUBLIC 88 | "${linenoise_SOURCE_DIR}" 89 | ) 90 | 91 | set(RELLIC_REPL "${PROJECT_NAME}-repl") 92 | 93 | add_executable(${RELLIC_REPL} 94 | "repl/Repl.cpp" 95 | ) 96 | 97 | target_link_libraries(${RELLIC_REPL} 98 | PRIVATE 99 | "${PROJECT_NAME}_cxx_settings" 100 | "${PROJECT_NAME}" 101 | gflags::gflags 102 | linenoise 103 | ) 104 | 105 | set(RELLIC_REPL "${RELLIC_REPL}" PARENT_SCOPE) 106 | 107 | # 108 | # rellic-dec2hex 109 | # 110 | set(RELLIC_DEC2HEX "${PROJECT_NAME}-dec2hex") 111 | 112 | add_executable(${RELLIC_DEC2HEX} 113 | "dec2hex/dec2hex.cpp" 114 | ) 115 | 116 | target_link_libraries(${RELLIC_DEC2HEX} 117 | PRIVATE 118 | "${PROJECT_NAME}_cxx_settings" 119 | "${PROJECT_NAME}" 120 | gflags::gflags 121 | ) 122 | 123 | set(RELLIC_DEC2HEX "${RELLIC_DEC2HEX}" PARENT_SCOPE) 124 | 125 | if(RELLIC_ENABLE_INSTALL) 126 | 127 | install( 128 | TARGETS 129 | ${RELLIC_DECOMP} 130 | ${RELLIC_HEADERGEN} 131 | ${RELLIC_XREF} 132 | ${RELLIC_REPL} 133 | EXPORT 134 | "${PROJECT_NAME}Targets" 135 | RUNTIME 136 | DESTINATION 137 | ${CMAKE_INSTALL_BINDIR} 138 | ) 139 | 140 | install( 141 | FILES 142 | "${CMAKE_CURRENT_SOURCE_DIR}/xref/www/index.html" 143 | "${CMAKE_CURRENT_SOURCE_DIR}/xref/www/main.js" 144 | "${CMAKE_CURRENT_SOURCE_DIR}/xref/www/style.css" 145 | DESTINATION 146 | "var/www/html" 147 | ) 148 | 149 | # 150 | # IDA UI plugin 151 | # 152 | install( 153 | FILES "${CMAKE_CURRENT_SOURCE_DIR}/plugins/ida-rellic.py" 154 | DESTINATION "share/rellic" 155 | ) 156 | 157 | endif(RELLIC_ENABLE_INSTALL) 158 | -------------------------------------------------------------------------------- /tools/dec2hex/dec2hex.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | DEFINE_string(input, "-", "Input C file."); 22 | DEFINE_string(output, "", "Output C file."); 23 | 24 | int main(int argc, char *argv[]) { 25 | std::stringstream usage; 26 | usage << std::endl 27 | << std::endl 28 | << " " << argv[0] << " \\" << std::endl 29 | << " --input INPUT_C_FILE \\" << std::endl 30 | << " --output OUTPUT_C_FILE \\" << std::endl 31 | << std::endl; 32 | 33 | google::InitGoogleLogging(argv[0]); 34 | google::InstallFailureSignalHandler(); 35 | google::SetUsageMessage(usage.str()); 36 | google::ParseCommandLineFlags(&argc, &argv, true); 37 | 38 | auto input_file = llvm::MemoryBuffer::getFileOrSTDIN(FLAGS_input); 39 | if (!input_file) { 40 | LOG(FATAL) << input_file.getError().message(); 41 | } 42 | auto ast_unit{clang::tooling::buildASTFromCodeWithArgs( 43 | input_file.get()->getBuffer(), {}, FLAGS_input, "rellic-dec2hex")}; 44 | auto &ast_ctx{ast_unit->getASTContext()}; 45 | 46 | auto heuristic = [](const llvm::APInt &value) { 47 | return value.getZExtValue() >= 16; 48 | }; 49 | 50 | if (FLAGS_output.empty()) { 51 | rellic::ConvertIntegerLiteralsToHex(ast_ctx, llvm::outs(), heuristic); 52 | } else { 53 | std::error_code ec; 54 | llvm::raw_fd_ostream os(FLAGS_output, ec); 55 | if (ec) { 56 | LOG(FATAL) << ec.message(); 57 | } 58 | rellic::ConvertIntegerLiteralsToHex(ast_ctx, os, heuristic); 59 | } 60 | 61 | google::ShutDownCommandLineFlags(); 62 | google::ShutdownGoogleLogging(); 63 | 64 | return EXIT_SUCCESS; 65 | } 66 | -------------------------------------------------------------------------------- /tools/decomp/Decomp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "rellic/BC/Util.h" 19 | #include "rellic/Decompiler.h" 20 | #include "rellic/Version.h" 21 | 22 | #ifndef LLVM_VERSION_STRING 23 | #define LLVM_VERSION_STRING LLVM_VERSION_MAJOR << "." << LLVM_VERSION_MINOR 24 | #endif 25 | 26 | DEFINE_string(input, "", "Input LLVM bitcode file."); 27 | DEFINE_string(output, "", "Output file."); 28 | DEFINE_bool(disable_z3, false, "Disable Z3 based AST tranformations."); 29 | DEFINE_bool(remove_phi_nodes, false, 30 | "Remove PHINodes from input bitcode before decompilation."); 31 | DEFINE_bool(lower_switch, false, 32 | "Remove SwitchInst by lowering them to branches."); 33 | 34 | DECLARE_bool(version); 35 | 36 | namespace { 37 | static llvm::Optional GetPCMetadata(llvm::Value* value) { 38 | auto inst{llvm::dyn_cast(value)}; 39 | if (!inst) { 40 | return llvm::Optional(); 41 | } 42 | 43 | auto pc{inst->getMetadata("pc")}; 44 | if (!pc) { 45 | return llvm::Optional(); 46 | } 47 | 48 | auto& cop{pc->getOperand(0U)}; 49 | auto cval{llvm::cast(cop)->getValue()}; 50 | return llvm::cast(cval)->getValue(); 51 | } 52 | } // namespace 53 | 54 | static void SetVersion(void) { 55 | std::stringstream version; 56 | 57 | auto vs = rellic::Version::GetVersionString(); 58 | if (0 == vs.size()) { 59 | vs = "unknown"; 60 | } 61 | version << vs << "\n"; 62 | if (!rellic::Version::HasVersionData()) { 63 | version << "No extended version information found!\n"; 64 | } else { 65 | version << "Commit Hash: " << rellic::Version::GetCommitHash() << "\n"; 66 | version << "Commit Date: " << rellic::Version::GetCommitDate() << "\n"; 67 | version << "Last commit by: " << rellic::Version::GetAuthorName() << " [" 68 | << rellic::Version::GetAuthorEmail() << "]\n"; 69 | version << "Commit Subject: [" << rellic::Version::GetCommitSubject() 70 | << "]\n"; 71 | version << "\n"; 72 | if (rellic::Version::HasUncommittedChanges()) { 73 | version << "Uncommitted changes were present during build.\n"; 74 | } else { 75 | version << "All changes were committed prior to building.\n"; 76 | } 77 | } 78 | version << "Using LLVM " << LLVM_VERSION_STRING << std::endl; 79 | 80 | google::SetVersionString(version.str()); 81 | } 82 | 83 | int main(int argc, char* argv[]) { 84 | std::stringstream usage; 85 | usage << std::endl 86 | << std::endl 87 | << " " << argv[0] << " \\" << std::endl 88 | << " --input INPUT_BC_FILE \\" << std::endl 89 | << " --output OUTPUT_C_FILE \\" << std::endl 90 | << std::endl 91 | 92 | // Print the version and exit. 93 | << " [--version]" << std::endl 94 | << std::endl; 95 | 96 | google::InitGoogleLogging(argv[0]); 97 | google::InstallFailureSignalHandler(); 98 | google::SetUsageMessage(usage.str()); 99 | SetVersion(); 100 | google::ParseCommandLineFlags(&argc, &argv, true); 101 | 102 | LOG_IF(ERROR, FLAGS_input.empty()) 103 | << "Must specify the path to an input LLVM bitcode file."; 104 | 105 | LOG_IF(ERROR, FLAGS_output.empty()) 106 | << "Must specify the path to an output C file."; 107 | 108 | if (FLAGS_input.empty() || FLAGS_output.empty()) { 109 | std::cerr << google::ProgramUsage(); 110 | return EXIT_FAILURE; 111 | } 112 | 113 | std::unique_ptr llvm_ctx(new llvm::LLVMContext); 114 | auto module{std::unique_ptr( 115 | rellic::LoadModuleFromFile(llvm_ctx.get(), FLAGS_input))}; 116 | 117 | std::error_code ec; 118 | llvm::raw_fd_ostream output(FLAGS_output, ec); 119 | CHECK(!ec) << "Failed to create output file: " << ec.message(); 120 | 121 | rellic::DecompilationOptions opts{}; 122 | opts.lower_switches = FLAGS_lower_switch; 123 | opts.remove_phi_nodes = FLAGS_remove_phi_nodes; 124 | 125 | auto result{rellic::Decompile(std::move(module), std::move(opts))}; 126 | if (result.Succeeded()) { 127 | auto value{result.TakeValue()}; 128 | value.ast->getASTContext().getTranslationUnitDecl()->print(output); 129 | } else { 130 | LOG(FATAL) << result.TakeError().message; 131 | } 132 | 133 | google::ShutDownCommandLineFlags(); 134 | google::ShutdownGoogleLogging(); 135 | 136 | return EXIT_SUCCESS; 137 | } 138 | -------------------------------------------------------------------------------- /tools/headergen/HeaderGen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "rellic/AST/DebugInfoCollector.h" 18 | #include "rellic/AST/StructGenerator.h" 19 | #include "rellic/AST/SubprogramGenerator.h" 20 | #include "rellic/BC/Util.h" 21 | #include "rellic/Version.h" 22 | 23 | #ifndef LLVM_VERSION_STRING 24 | #define LLVM_VERSION_STRING LLVM_VERSION_MAJOR << "." << LLVM_VERSION_MINOR 25 | #endif 26 | 27 | DEFINE_string(input, "", "Input file."); 28 | DEFINE_string(output, "", "Output file."); 29 | DEFINE_bool(generate_prototypes, true, "Generate function prototypes."); 30 | 31 | DECLARE_bool(version); 32 | 33 | static void SetVersion(void) { 34 | std::stringstream version; 35 | 36 | auto vs = rellic::Version::GetVersionString(); 37 | if (0 == vs.size()) { 38 | vs = "unknown"; 39 | } 40 | version << vs << "\n"; 41 | if (!rellic::Version::HasVersionData()) { 42 | version << "No extended version information found!\n"; 43 | } else { 44 | version << "Commit Hash: " << rellic::Version::GetCommitHash() << "\n"; 45 | version << "Commit Date: " << rellic::Version::GetCommitDate() << "\n"; 46 | version << "Last commit by: " << rellic::Version::GetAuthorName() << " [" 47 | << rellic::Version::GetAuthorEmail() << "]\n"; 48 | version << "Commit Subject: [" << rellic::Version::GetCommitSubject() 49 | << "]\n"; 50 | version << "\n"; 51 | if (rellic::Version::HasUncommittedChanges()) { 52 | version << "Uncommitted changes were present during build.\n"; 53 | } else { 54 | version << "All changes were committed prior to building.\n"; 55 | } 56 | } 57 | version << "Using LLVM " << LLVM_VERSION_STRING << std::endl; 58 | 59 | google::SetVersionString(version.str()); 60 | } 61 | 62 | int main(int argc, char* argv[]) { 63 | std::stringstream usage; 64 | usage << std::endl 65 | << std::endl 66 | << " " << argv[0] << " \\" << std::endl 67 | << " --input INPUT_FILE \\" << std::endl 68 | << " --output OUTPUT_FILE \\" << std::endl 69 | << std::endl 70 | 71 | // Print the version and exit. 72 | << " [--version]" << std::endl 73 | << std::endl; 74 | 75 | google::InitGoogleLogging(argv[0]); 76 | google::InstallFailureSignalHandler(); 77 | google::SetUsageMessage(usage.str()); 78 | SetVersion(); 79 | google::ParseCommandLineFlags(&argc, &argv, true); 80 | 81 | LOG_IF(ERROR, FLAGS_input.empty()) 82 | << "Must specify the path to an input file."; 83 | 84 | LOG_IF(ERROR, FLAGS_output.empty()) 85 | << "Must specify the path to an output file."; 86 | 87 | if (FLAGS_input.empty() || FLAGS_output.empty()) { 88 | std::cerr << google::ProgramUsage(); 89 | return EXIT_FAILURE; 90 | } 91 | 92 | auto llvm_ctx{std::make_unique()}; 93 | auto module{rellic::LoadModuleFromFile(llvm_ctx.get(), FLAGS_input)}; 94 | auto dic{std::make_unique()}; 95 | dic->visit(module); 96 | std::vector args{"-Wno-pointer-to-int-cast", "-Wno-pointer-sign", 97 | "-target", module->getTargetTriple()}; 98 | auto ast_unit{clang::tooling::buildASTFromCodeWithArgs("", args, "out.c")}; 99 | rellic::StructGenerator strctgen(*ast_unit); 100 | rellic::SubprogramGenerator subgen(*ast_unit, strctgen); 101 | auto types{dic->GetTypes()}; 102 | strctgen.GenerateDecls(types.begin(), types.end()); 103 | 104 | if (FLAGS_generate_prototypes) { 105 | for (auto func : dic->GetSubprograms()) { 106 | subgen.VisitSubprogram(func); 107 | } 108 | } 109 | 110 | std::error_code ec; 111 | // FIXME(surovic): Figure out if the fix below works. 112 | // llvm::raw_fd_ostream output(FLAGS_output, ec, llvm::sys::fs::F_Text); 113 | llvm::raw_fd_ostream output(FLAGS_output, ec); 114 | ast_unit->getASTContext().getTranslationUnitDecl()->print(output); 115 | CHECK(!ec) << "Failed to create output file: " << ec.message(); 116 | 117 | google::ShutDownCommandLineFlags(); 118 | google::ShutdownGoogleLogging(); 119 | 120 | return EXIT_SUCCESS; 121 | } 122 | -------------------------------------------------------------------------------- /tools/headergen/README.md: -------------------------------------------------------------------------------- 1 | # `rellic-headergen` 2 | 3 | ## What is it? 4 | This utility generates C code that describes the layout of data structures as defined by the DWARF metadata contained inside of LLVM modules. 5 | 6 | ## Why would I use it? 7 | `rellic-headergen` is useful when needing to "flatten" the layout of C++ code for example, which can contain classes and complex inheritance relations. The output is valid C code that matches the memory layout of the original C++. 8 | 9 | ## How do I use it? 10 | After compiling `rellic`, you can use `rellic-headergen` like so: 11 | ```sh 12 | $ rellic-headergen --input path/to/module.bc --output layout.c 13 | ``` 14 | 15 | As an example, compiling the following code 16 | ```c++ 17 | class Vehicle { 18 | public: 19 | virtual ~Vehicle(void); 20 | virtual void Drive(float speed) = 0; 21 | }; 22 | 23 | enum Color { 24 | kRed, 25 | kBlack, 26 | kWhite, 27 | kBlue 28 | }; 29 | 30 | class Car : public Vehicle { 31 | private: 32 | Color color; 33 | float mileage; 34 | 35 | public: 36 | explicit Car(Color color_); 37 | 38 | virtual ~Car(void); 39 | void Drive(float speed) override; 40 | }; 41 | 42 | void force(Car *c) { 43 | c->Drive(10.0); 44 | } 45 | ``` 46 | 47 | using clang and then running `rellic-headergen` 48 | ```sh 49 | $ clang++ -std=c++17 -O0 -gfull -emit-llvm -c test.cpp -o test.bc 50 | $ rellic-headergen --input test.bc --output /dev/stderr 51 | ``` 52 | 53 | produces the following C code 54 | ```c 55 | struct Car_0; 56 | enum Color_1 { 57 | kRed_0 = 0UL, 58 | kBlack_1 = 1UL, 59 | kWhite_2 = 2UL, 60 | kBlue_3 = 3UL 61 | }; 62 | struct Vehicle_2; 63 | struct Car_0 { 64 | int (**_vptr_Vehicle0)(void); 65 | enum Color_1 color1; 66 | float mileage2; 67 | }; 68 | struct Vehicle_2 { 69 | int (**_vptr_Vehicle0)(void); 70 | }; 71 | void _Z5forceP3Car(struct Car_0 *arg1); 72 | ``` -------------------------------------------------------------------------------- /tools/xref/Printer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | void PrintDecl(clang::Decl* Decl, const clang::PrintingPolicy& Policy, 20 | int Indentation, llvm::raw_ostream& Out); 21 | void PrintDeclGroup(clang::Decl** Begin, unsigned NumDecls, 22 | llvm::raw_ostream& Out, const clang::PrintingPolicy& Policy, 23 | unsigned Indentation); 24 | void PrintStmt(clang::Stmt* Stmt, llvm::raw_ostream& Out, 25 | const clang::PrintingPolicy& Policy, int Indentation = 0, 26 | const clang::ASTContext* Context = nullptr, 27 | clang::PrinterHelper* Helper = nullptr); 28 | void PrintType(clang::QualType Type, llvm::raw_ostream& Out, 29 | const clang::PrintingPolicy& Policy, 30 | const llvm::Twine& PlaceHolder = llvm::Twine(), 31 | unsigned Indentation = 0); 32 | std::string GetQualTypeAsString(clang::QualType Type, 33 | const clang::PrintingPolicy& Policy); 34 | std::string GetTypeAsString(const clang::Type* Ty, 35 | const clang::PrintingPolicy& Policy); 36 | void PrintQualifiers(const clang::Qualifiers& Qualifiers, llvm::raw_ostream& OS, 37 | const clang::PrintingPolicy& Policy, 38 | bool appendSpaceIfNonEmpty = false); 39 | std::string GetQualifiersAsString(const clang::Qualifiers& Qualifiers); 40 | std::string GetQualifiersAsString(const clang::Qualifiers& Qualifiers, 41 | const clang::PrintingPolicy& Policy); -------------------------------------------------------------------------------- /tools/xref/README.md: -------------------------------------------------------------------------------- 1 | # `rellic-xref` 2 | 3 | ## What is it? 4 | 5 | `rellix-xref` is an interactive, web-based interface for Rellic. Whereas `rellic-decomp` provides a batch approach to decompiling LLVM modules, `rellic-xref` aims to be a more exploratory tool. Its purpose is to bring more insight into the inner workings of Rellic's lifting process and refinement passes by allowing the user the choice of which passes to apply, and in what order. 6 | It also provides visual feedback regarding the way the original bitcode has been lifted into C source code: hovering over parts of the C AST will highlight the bitcode that generated it, and vice versa. 7 | 8 | ## How do I use it? 9 | 10 | After compiling Rellic, launch the `rellic-xref` executable: 11 | 12 | $ rellic-xref --port=8080 --home=/path/to/rellic/tools/xref/www 13 | 14 | This will spawn a server on port 8080 that will serve a web interface to the Rellic decompilation engine. 15 | 16 | The complete list of available command line options is: 17 | * `--address`: Tells `rellic-xref` to listen for connections from a specific address. Defaults to `0.0.0.0`, which means all addresses are considered valid. 18 | * `--port`: TCP port on which the HTTP server will listen. Defaults to `80`. 19 | * `--home`: Path where `rellic-xref`'s assets are found. Should point to the `www` directory that is supplied alongside this README. 20 | * `--angha`: Path to a directory containing AnghaBench test files. Supplying the files allows the server to load them directly without uploading through the interface. If not needed, point this to an empty directory. 21 | 22 | `rellic-xref` will also accept any arguments from `gflags` and `glog` like `--logtostderr` and `--help`. 23 | 24 | As an example, at Trail of Bits we have an instance of `rellic-xref` running on a private VPS. To provide automatic restarts in the event of crashes, it is configured as a `systemd` service. The following is an example of what such a service file would look like: 25 | 26 | ```systemd 27 | [Unit] 28 | Description=rellic-xref daemon 29 | After=network-online.target 30 | 31 | [Service] 32 | Type=simple 33 | ExecStart=/path/to/rellic-xref --address=0.0.0.0 --port=80 --home=/path/to/www --logtostderr=1 --angha=/path/to/angha/bitcode 34 | Restart=on-failure 35 | StandardError=journal 36 | 37 | [Install] 38 | WantedBy=multi-user.target 39 | ``` 40 | 41 | ## Should I expose this publicly to the internet? 42 | 43 | NO! This tool is only meant for local / intranet use, **never** expose it to untrusted users. 44 | -------------------------------------------------------------------------------- /tools/xref/www/style.css: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | width: 100%; 4 | height: 100vh; 5 | margin: 0; 6 | padding: 0; 7 | font-family: sans-serif; 8 | } 9 | 10 | .hover { 11 | background-color: rgba(0, 0, 0, 0.1); 12 | } 13 | 14 | .hover-sameaddr { 15 | text-decoration: underline green wavy; 16 | } 17 | 18 | .logo { 19 | font-size: xx-large; 20 | margin-right: 1em; 21 | } 22 | 23 | #app { 24 | width: 100%; 25 | height: 100%; 26 | margin: 0; 27 | padding: 0; 28 | display: flex; 29 | flex-direction: column; 30 | } 31 | 32 | header { 33 | flex-grow: 0; 34 | padding: 0.5em; 35 | } 36 | 37 | main { 38 | flex-grow: 1; 39 | max-height: 85vh; 40 | } 41 | 42 | footer { 43 | flex-grow: 0; 44 | padding: 0.5em; 45 | } 46 | 47 | .splitpanes__pane { 48 | border: 1px solid grey; 49 | overflow: auto; 50 | } 51 | 52 | pre { 53 | margin: 0.5em; 54 | padding: 0.5em; 55 | } 56 | 57 | .clang.keyword, 58 | .llvm.keyword { 59 | color: blue; 60 | } 61 | 62 | .clang.typename, 63 | .llvm.typename { 64 | color: purple; 65 | } 66 | 67 | .clang.string-literal, 68 | .clang.character-literal, 69 | .llvm.string-literal { 70 | color: maroon; 71 | } 72 | 73 | .clang.number, 74 | .llvm.number { 75 | color: darkcyan; 76 | } 77 | 78 | .llvm.comment { 79 | color: green; 80 | } 81 | 82 | .list-container { 83 | border: 1px solid grey; 84 | border-radius: 0.3em; 85 | background-color: #00000011; 86 | margin-top: 0.2em; 87 | margin-left: 0.2em; 88 | margin-right: 0.5em; 89 | margin-bottom: 0.5em; 90 | } 91 | 92 | .list-item { 93 | margin: 0.2em; 94 | } 95 | 96 | .list-title { 97 | padding: 0.2em; 98 | } 99 | 100 | .list-bottom { 101 | padding: 0.2em; 102 | } 103 | 104 | .dir-entry { 105 | padding-left: 0.5em; 106 | cursor: pointer; 107 | } 108 | 109 | .dir-entry>summary { 110 | list-style-type: '\1F5C0\FE0E'; 111 | } 112 | 113 | .dir-entry[open]>summary { 114 | list-style-type: '\1F5C1\FE0E'; 115 | } 116 | 117 | .file-entry { 118 | margin-left: 0.5em; 119 | border: none; 120 | background-color: inherit; 121 | cursor: pointer; 122 | display: block; 123 | } 124 | 125 | .file-entry::before { 126 | content: '\1F5CE\FE0E'; 127 | padding-right: 0.5em; 128 | font-size: large; 129 | } 130 | 131 | dialog { 132 | width: 50vw; 133 | } -------------------------------------------------------------------------------- /unittests/AST/StructGenerator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "rellic/AST/StructGenerator.h" 10 | 11 | #include 12 | 13 | #include "Util.h" 14 | #include "rellic/AST/ASTBuilder.h" 15 | 16 | template 17 | static clang::DeclRefExpr *GetDeclRef(rellic::ASTBuilder &ast, 18 | clang::DeclContext *decl_ctx, 19 | const std::string &name) { 20 | auto ref{ast.CreateDeclRef(GetDecl(decl_ctx, name))}; 21 | REQUIRE(ref != nullptr); 22 | return ref; 23 | } 24 | 25 | TEST_SUITE("StructGenerator::GetAccessor") { 26 | SCENARIO("Create accessors for non packed struct") { 27 | GIVEN("Struct definition s") { 28 | std::vector args{"-target", "x86_64-pc-linux-gnu"}; 29 | auto unit{GetASTUnit("struct s { char c; int i; } x;", args)}; 30 | auto &ctx{unit->getASTContext()}; 31 | auto tudecl{ctx.getTranslationUnitDecl()}; 32 | rellic::ASTBuilder ast(*unit); 33 | rellic::StructGenerator gen(*unit); 34 | auto var{GetDeclRef(ast, tudecl, "x")}; 35 | auto strct{GetDecl(tudecl, "s")}; 36 | THEN("return correct accessors") { 37 | auto accessors_i{gen.GetAccessor(var, strct, 32, 32)}; 38 | CHECK_EQ(accessors_i.size(), 1); 39 | 40 | auto accessors_c{gen.GetAccessor(var, strct, 0, 8)}; 41 | CHECK_EQ(accessors_c.size(), 1); 42 | 43 | auto accessors_invalid{gen.GetAccessor(var, strct, 8, 32)}; 44 | CHECK_EQ(accessors_invalid.size(), 0); 45 | } 46 | } 47 | } 48 | 49 | SCENARIO("Create accessors for non packed bitfield struct") { 50 | GIVEN("Struct definition s") { 51 | std::vector args{"-target", "x86_64-pc-linux-gnu"}; 52 | auto unit{GetASTUnit("struct s { int i : 3; int j : 3; } x;", args)}; 53 | auto &ctx{unit->getASTContext()}; 54 | auto tudecl{ctx.getTranslationUnitDecl()}; 55 | rellic::ASTBuilder ast(*unit); 56 | rellic::StructGenerator gen(*unit); 57 | auto var{GetDeclRef(ast, tudecl, "x")}; 58 | auto strct{GetDecl(tudecl, "s")}; 59 | THEN("return correct accessors") { 60 | auto accessors_i{gen.GetAccessor(var, strct, 0, 3)}; 61 | CHECK_EQ(accessors_i.size(), 1); 62 | 63 | auto accessors_j{gen.GetAccessor(var, strct, 3, 3)}; 64 | CHECK_EQ(accessors_j.size(), 1); 65 | 66 | auto accessors_invalid{gen.GetAccessor(var, strct, 0, 8)}; 67 | CHECK_EQ(accessors_invalid.size(), 0); 68 | } 69 | } 70 | } 71 | 72 | SCENARIO("Create accessors for union") { 73 | GIVEN("Union definition u") { 74 | std::vector args{"-target", "x86_64-pc-linux-gnu"}; 75 | auto unit{GetASTUnit("union u { int i; int j; char c; } x;", args)}; 76 | auto &ctx{unit->getASTContext()}; 77 | auto tudecl{ctx.getTranslationUnitDecl()}; 78 | rellic::ASTBuilder ast(*unit); 79 | rellic::StructGenerator gen(*unit); 80 | auto var{GetDeclRef(ast, tudecl, "x")}; 81 | auto strct{GetDecl(tudecl, "u")}; 82 | THEN("return correct accessors") { 83 | auto accessors_ij{gen.GetAccessor(var, strct, 0, 32)}; 84 | CHECK_EQ(accessors_ij.size(), 2); 85 | 86 | auto accessors_c{gen.GetAccessor(var, strct, 0, 8)}; 87 | CHECK_EQ(accessors_c.size(), 1); 88 | 89 | auto accessors_invalid{gen.GetAccessor(var, strct, 32, 8)}; 90 | CHECK_EQ(accessors_invalid.size(), 0); 91 | } 92 | } 93 | } 94 | 95 | SCENARIO("Create accessors for nested struct") { 96 | GIVEN("Struct definition s") { 97 | std::vector args{"-target", "x86_64-pc-linux-gnu"}; 98 | auto unit{GetASTUnit( 99 | "struct s { int i; union { struct { int j; char c; }; int k; }; } x;", 100 | args)}; 101 | auto &ctx{unit->getASTContext()}; 102 | auto tudecl{ctx.getTranslationUnitDecl()}; 103 | rellic::ASTBuilder ast(*unit); 104 | rellic::StructGenerator gen(*unit); 105 | auto var{GetDeclRef(ast, tudecl, "x")}; 106 | auto strct{GetDecl(tudecl, "s")}; 107 | THEN("return correct accessors") { 108 | auto accessors_i{gen.GetAccessor(var, strct, 0, 32)}; 109 | CHECK_EQ(accessors_i.size(), 1); 110 | 111 | auto accessors_jk{gen.GetAccessor(var, strct, 32, 32)}; 112 | CHECK_EQ(accessors_jk.size(), 2); 113 | 114 | auto accessors_c{gen.GetAccessor(var, strct, 64, 8)}; 115 | CHECK_EQ(accessors_c.size(), 1); 116 | 117 | auto accessors_invalid{gen.GetAccessor(var, strct, 32, 8)}; 118 | CHECK_EQ(accessors_invalid.size(), 0); 119 | } 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /unittests/AST/Util.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "Util.h" 10 | 11 | #include 12 | 13 | std::unique_ptr GetASTUnit(const char *code) { 14 | auto unit{clang::tooling::buildASTFromCode(code, "out.c")}; 15 | REQUIRE(unit != nullptr); 16 | return unit; 17 | } 18 | 19 | std::unique_ptr GetASTUnit( 20 | const char *code, const std::vector &args) { 21 | auto unit{clang::tooling::buildASTFromCodeWithArgs(code, args, "out.c")}; 22 | REQUIRE(unit != nullptr); 23 | return unit; 24 | } -------------------------------------------------------------------------------- /unittests/AST/Util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | std::unique_ptr GetASTUnit(const char *code = ""); 15 | std::unique_ptr GetASTUnit( 16 | const char *code, const std::vector &args); 17 | 18 | template 19 | T *GetDecl(clang::DeclContext *decl_ctx, const std::string &name) { 20 | auto &ctx{decl_ctx->getParentASTContext()}; 21 | auto lookup_result{decl_ctx->noload_lookup(&ctx.Idents.get(name))}; 22 | REQUIRE(std::distance(lookup_result.begin(), lookup_result.end()) == 1); 23 | auto decl{clang::dyn_cast(lookup_result.front())}; 24 | REQUIRE(decl != nullptr); 25 | return decl; 26 | } -------------------------------------------------------------------------------- /unittests/AST/Z3ConvVisitor.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "rellic/AST/Z3ConvVisitor.h" 10 | 11 | #include 12 | 13 | #include "Util.h" 14 | 15 | TEST_SUITE("Z3ConvVisitor::VisitFunctionDecl") { 16 | SCENARIO("Create a z3::func_decl for a given clang::FunctionDecl") { 17 | GIVEN("Function `int f(int a, char b);`") { 18 | auto c_unit{GetASTUnit("int f(int a, char b);")}; 19 | z3::context z_ctx; 20 | rellic::Z3ConvVisitor conv(*c_unit, &z_ctx); 21 | auto c_tudecl{c_unit->getASTContext().getTranslationUnitDecl()}; 22 | auto c_fdecl{GetDecl(c_tudecl, "f")}; 23 | THEN("return an uninterpreted function `f(bv32 bv8 bv32)`") { 24 | auto z_fdecl{conv.GetOrCreateZ3Decl(c_fdecl)}; 25 | CHECK(z_fdecl.decl_kind() == Z3_OP_UNINTERPRETED); 26 | CHECK(z_fdecl.name().str() == c_fdecl->getNameAsString()); 27 | CHECK(z_fdecl.arity() == c_fdecl->getNumParams()); 28 | CHECK(z_fdecl.range().bv_size() == 32U); 29 | CHECK(z_fdecl.domain(0U).bv_size() == 32U); 30 | CHECK(z_fdecl.domain(1U).bv_size() == 8U); 31 | } 32 | } 33 | } 34 | } 35 | 36 | TEST_SUITE("Z3ConvVisitor::VisitCallExpr") { 37 | SCENARIO("Create a z3::expr for a given clang::CallExpr") { 38 | GIVEN("Functions `void f1(int a, char b); void f2(void){ f1(0, 1); }`") { 39 | auto c_unit{ 40 | GetASTUnit("void f1(int a, char b);" 41 | "void f2(void){ f1(0, 1); }")}; 42 | z3::context z_ctx; 43 | rellic::Z3ConvVisitor conv(*c_unit, &z_ctx); 44 | auto c_tudecl{c_unit->getASTContext().getTranslationUnitDecl()}; 45 | auto c_fdecl{GetDecl(c_tudecl, "f2")}; 46 | auto c_func_body{clang::cast(c_fdecl->getBody())}; 47 | auto c_call{clang::cast(*c_func_body->body_begin())}; 48 | THEN("return an uninterpreted function application `(Call id f1 0 1)`") { 49 | auto z_call{conv.GetOrCreateZ3Expr(c_call)}; 50 | CHECK(z_call.decl().name().str() == "Call"); 51 | llvm::FoldingSetNodeID id; 52 | c_call->Profile(id, c_unit->getASTContext(), /*Canonical=*/true); 53 | CHECK(z_call.arg(0U).simplify().get_numeral_uint64() == 54 | id.ComputeHash()); 55 | auto z_callee{conv.GetOrCreateZ3Expr(c_call->getCallee())}; 56 | CHECK(z3::eq(z_call.arg(1U), z_callee)); 57 | CHECK(z_call.arg(2U).simplify().get_numeral_uint64() == 0U); 58 | CHECK(z_call.arg(3U).simplify().get_numeral_uint64() == 1U); 59 | } 60 | } 61 | } 62 | } 63 | 64 | // TEST_SUITE("Z3ConvVisitor::HandleZ3Call") { 65 | // SCENARIO("Create a clang::CallExpr for a `(Call 1 f 2 3)` z3::expr") { 66 | // GIVEN("Uninterpreted z3 function `f(bv32 bv32 bv32)`") { 67 | // auto c_unit{GetASTUnit("")}; 68 | // z3::context z_ctx; 69 | // rellic::Z3ConvVisitor conv(*c_unit, &z_ctx); 70 | // auto z_sort{z_ctx.bv_sort(32U)}; 71 | // auto z_func_decl{z_ctx.function("f", z_sort, z_sort, z_sort)}; 72 | // auto z_func{z3::as_array(z_func_decl)}; 73 | // auto z_call{z_ctx.function("Call", z_sort, z_func.get_sort(), z_sort, 74 | // z_sort, z_func.get_sort().array_range())}; 75 | // GIVEN("Application `(Call 1 f 2 3)`") { 76 | // auto z_bv_1{z_ctx.bv_val(1U, 32U)}; 77 | // auto z_bv_2{z_ctx.bv_val(2U, 32U)}; 78 | // auto z_bv_3{z_ctx.bv_val(3U, 32U)}; 79 | // auto z_app{z_call(z_bv_1, z_func, z_bv_2, z_bv_3)}; 80 | // THEN("return a `f(2U, 3U)` clang::CallExpr") { 81 | // auto c_call{conv.GetOrCreateCExpr(z_app)}; 82 | // CHECK(c_call != nullptr); 83 | // } 84 | // } 85 | // } 86 | // } 87 | // } 88 | -------------------------------------------------------------------------------- /unittests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # rellic-unittest 3 | # 4 | 5 | set(RELLIC_UNITTEST ${PROJECT_NAME}-unittest) 6 | 7 | add_executable(${RELLIC_UNITTEST} 8 | AST/ASTBuilder.cpp 9 | AST/StructGenerator.cpp 10 | AST/Util.cpp 11 | UnitTest.cpp 12 | ) 13 | 14 | target_link_libraries(${RELLIC_UNITTEST} PRIVATE 15 | "${PROJECT_NAME}_cxx_settings" 16 | "${PROJECT_NAME}" 17 | z3::libz3 18 | doctest::doctest 19 | ) 20 | 21 | target_compile_options(${RELLIC_UNITTEST} PRIVATE -fexceptions) 22 | 23 | add_test( 24 | NAME test_${RELLIC_UNITTEST} 25 | COMMAND "$" 26 | WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 27 | ) 28 | -------------------------------------------------------------------------------- /unittests/UnitTest.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-present, Trail of Bits, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed in accordance with the terms specified in 6 | * the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 10 | #include --------------------------------------------------------------------------------