mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
PowerPC Support (#645)
* Add skeleton for PPC
* Copyright notices
* Fill in some details for the PPC arch
* Start building a (wrong) PPC runtime
* Begin populating state structure
* First pass for EIS state structure
* Map registers to Sleigh register names
* More fixes
* add optional param
* Create handle unsupported and invalid instruction isels
* Correct typo
* Get a basic `remill-lift` invocation running without failure
* Fix capitalisation
* Set vle context reg
* Fix SleighDecoder signatures
* Set VLE context register in the Sleigh engine in addition to our
internal context reg mapping
* Capitalize reg names
* Add the flag registers for XER and CR
* Rename bitflag structures in PPC state
* PPC Sleigh patches (#643)
* Modified sleigh patch script to generate patches for multiple .sinc files
* update README with new examples of sleigh patch script invocation
* add ppc register definition
* add ppc sleigh patches
* fix issue with remill_insn_size definition
* regenerate sleigh patches for PPC
* update CMakeLists.txt to include PPC patches
* Add TEA signal as a register in the PPC state
* Uppercase the stack pointer register name
* Fix PPC instruction sizes
* initial PPC tests
* remove duplicate tests
* fix tests for e_stmvgprw/e_ldmvgprw
* add tests for loading/storing from special registers
* add tests with internal conditionals in pcode
* fix for pc reg and addr width not being the same... I suspect this issue is going to come up elsewhere
* add heuristic for flow from normal intrainstruction flow
* rework tests to allow testing for different sized registers
* add tests for overflow and record add
* fix bug with log printout
* add intrafunction control flow lifting
* handle edge case where there is no pcode op at the zero index
* Fix another inconsistency with mismatching address and PC reg size
* Allocate unique ptrs in the entry block
* Fix `INT_LEFT` and `INT_RIGHT` impl where shift exceeds bit width
* fix supiece lift?
* Add PPC emulate instruction to hyper call
* fix for pc reg and addr width not being the same... I suspect this issue is going to come up elsewhere
* add heuristic for flow from normal intrainstruction flow
* add intrafunction control flow lifting
* handle edge case where there is no pcode op at the zero index
* Fix another inconsistency with mismatching address and PC reg size
* fix supiece lift?
* Allocate unique ptrs in the entry block
* Fix `INT_LEFT` and `INT_RIGHT` impl where shift exceeds bit width
* fix int2float semantics
should use appropriate sized float based on the output size
* add tests for lifting int2float
* fix INT_{LEFT,RIGHT} semantics
should be `ICmpSGE` instead of `ICmpSGT`
* add cr0-7 registers
* fix formatting
* fix conditional branch test
* add test for compare
* re-enable rotate left word immediate and mask test
* genericize TestSpecOutput
* explicit instruction data size
* add test for syscall/callother (disabled)
* add tests for store/load word
* add test to convert from float to int
* specify intrinsic arg type, fixes null deref
* Add PPC emulate instruction to hyper call
* add headers + formatting
* remove old comment
* Map CRALL register
* Add basic LLVM data layout that specifies 32-bit addresses
* Remove unused variables
* convert auto* to auto when possible
* RegisterPrecondition -> RegisterCondition
* fix variable name
* convert any to variant
* use std::move
* bump to c++20, use concepts
* set arch in constructor since class isn't generic anyways
* formatting
* make type aliases
* bump cxx-common
* add comment
* clang format
* throw exception if register not found
* use const ref
* use shorthand for lambda capture values
* Add more detail to data layout to include proper stack alignment
* Compare to the correct size for SUBPIECE impl
* Add Sleigh message to error
* throw exception in else case
* throw runtime error if register value has incorrect type
* use reference instead of value
* get rid of unnecessary type alias
* formatting
* Propagate VLE context reg value into Sleigh
* Remove unnecessary whitespace
* Remove stale TODO and NOTE comments
* add additional parameter to test runner to specify decoding context
* drop llvm 14, bump macos version
* bump cxx-common, fix ci.yml mac build
* add test for unconditional relative negative branch
* add missing space to pcode debug log
* fix bug due to unordered_map, iteration order matters
* add error log in case we aren't able to adjust PC value
* use helper for getting register reference
* Revert "add optional param"
This reverts commit 51ed49f8cf.
* Remove remaining LLVM 14 compatibility code and configuration
* Add padding between CR and XER flags
* Use `enum class`
* Remove void cast
* Remove unnecessary variable
* Use initialiser lists where appropriate
* Remove redundant `else`
* Prefer `CHECK` over `assert`
* Polish PowerPC function initialisation with lambda
* zero out xer_so to fix tests
* log error when we see claim_eq with no usages
* Collapse namespace blocks
* Remove unnecessary `this->`
* Use `auto` where appropriate
* Remove unnecessary `else`
* Use `emplace` over `insert` for `std::map`
Co-authored-by: lkorenc <lukas.korencik@trailofbits.com>
* Use `constexpr` for VLE reg name
* Use module verification util
* Add `VerifyFunction` util and use where applicable
* Extract lambda to improve readability of flow categorisation
* Use type alias for context values
* Introduce type alias for block exit
* Create type alias for optional branch taken
* Refactor `PcodeCFGBuilder`
* Use lambda to avoid conditional mutation
* Extract duplicated bit-shift code generation into helper
* Simplify flow with ternary
* Add `GetBlock` helper
* Rename variables
* Move statement for clarity
* Create helpers for working with Sleigh context register values
* Convert loop to `std::copy`
* Add a comment explaining the use of set to de-duplicate and sort
* Refactor `IntraProcTransferCollector`
* Expose static method to easily use `IntraProcTransferCollector`
* Rename PPC related variables to include address width
* add docs to intrainstructionindex
* remove llvm 14 ifdefs
* don't log error if no claim_eqs were used
* update comments
* Cleanup exit visitors
---------
Co-authored-by: 2over12 <ian.smith@trailofbits.com>
Co-authored-by: William Tan <1284324+Ninja3047@users.noreply.github.com>
Co-authored-by: lkorenc <lukas.korencik@trailofbits.com>
This commit is contained in:
@@ -23,7 +23,7 @@ jobs:
|
||||
matrix:
|
||||
image:
|
||||
- { name: 'ubuntu', tag: '20.04' }
|
||||
llvm: ['14', '15']
|
||||
llvm: ['15']
|
||||
|
||||
runs-on: ubuntu-20.04
|
||||
container:
|
||||
@@ -103,9 +103,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ['macos-11']
|
||||
llvm: ['14', '15']
|
||||
llvm: ['15']
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: macos-12
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -235,7 +235,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
llvm: ["14", "15"]
|
||||
llvm: ["15"]
|
||||
ubuntu: ["20.04"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
+11
-3
@@ -44,11 +44,11 @@ llvm_map_components_to_libnames(llvm_libs
|
||||
support core irreader
|
||||
bitreader bitwriter
|
||||
passes asmprinter
|
||||
aarch64codegen aarch64asmparser
|
||||
aarch64info aarch64desc aarch64codegen aarch64asmparser
|
||||
armcodegen armasmparser
|
||||
interpreter mcjit
|
||||
nvptxdesc
|
||||
x86codegen x86asmparser
|
||||
x86info x86codegen x86asmparser
|
||||
sparccodegen sparcasmparser
|
||||
webassemblydesc)
|
||||
message(STATUS "LLVM Libraries: ${llvm_libs}")
|
||||
@@ -72,6 +72,7 @@ set(REMILL_BUILD_SEMANTICS_DIR_AARCH32 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/AAr
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_AARCH64 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/AArch64/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_SPARC32 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/SPARC32/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_SPARC64 "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/SPARC64/Runtime")
|
||||
set(REMILL_BUILD_SEMANTICS_DIR_PPC64_32ADDR "${CMAKE_CURRENT_BINARY_DIR}/lib/Arch/PPC/Runtime")
|
||||
set(REMILL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
set(REMILL_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lib")
|
||||
|
||||
@@ -87,7 +88,8 @@ find_package(glog CONFIG REQUIRED)
|
||||
find_package(gflags CONFIG REQUIRED)
|
||||
|
||||
set(sleigh_ENABLE_TESTS OFF)
|
||||
set(sleigh_ADDITIONAL_PATCHES "${CMAKE_CURRENT_SOURCE_DIR}/patches/sleigh/x86-ia.patch;${CMAKE_CURRENT_SOURCE_DIR}/patches/sleigh/arm-thumb.patch" CACHE STRING "" FORCE)
|
||||
file(GLOB sleigh_patches "${CMAKE_CURRENT_SOURCE_DIR}/patches/sleigh/*.patch")
|
||||
set(sleigh_ADDITIONAL_PATCHES "${sleigh_patches}" CACHE STRING "" FORCE)
|
||||
|
||||
# GHIDRA SLEIGH
|
||||
FetchContent_Declare(sleigh
|
||||
@@ -202,6 +204,7 @@ target_compile_definitions(remill_settings INTERFACE
|
||||
"REMILL_BUILD_SEMANTICS_DIR_AARCH64=\"${REMILL_BUILD_SEMANTICS_DIR_AARCH64}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_SPARC32=\"${REMILL_BUILD_SEMANTICS_DIR_SPARC32}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_SPARC64=\"${REMILL_BUILD_SEMANTICS_DIR_SPARC64}\""
|
||||
"REMILL_BUILD_SEMANTICS_DIR_PPC64_32ADDR=\"${REMILL_BUILD_SEMANTICS_DIR_PPC64_32ADDR}\""
|
||||
)
|
||||
|
||||
target_link_libraries(remill_settings INTERFACE
|
||||
@@ -295,6 +298,11 @@ if(REMILL_ENABLE_TESTING)
|
||||
add_subdirectory(tests/Thumb)
|
||||
endif()
|
||||
|
||||
if(REMILL_ENABLE_TESTING_SLEIGH_PPC)
|
||||
message(STATUS "ppc tests enabled")
|
||||
add_subdirectory(tests/PPC)
|
||||
endif()
|
||||
|
||||
if(REMILL_ENABLE_TESTING_X86)
|
||||
message(STATUS "X86 tests enabled")
|
||||
add_subdirectory(tests/X86)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Choose your LLVM version
|
||||
ARG LLVM_VERSION=13
|
||||
ARG LLVM_VERSION=15
|
||||
ARG ARCH=amd64
|
||||
ARG UBUNTU_VERSION=20.04
|
||||
ARG DISTRO_BASE=ubuntu${UBUNTU_VERSION}
|
||||
|
||||
@@ -40,8 +40,8 @@ Most of Remill's dependencies can be provided by the [cxx-common](https://github
|
||||
| [Google Flags](https://github.com/google/glog) | Latest |
|
||||
| [Google Log](https://github.com/google/glog) | Latest |
|
||||
| [Google Test](https://github.com/google/googletest) | Latest |
|
||||
| [LLVM](http://llvm.org/) | 14 |
|
||||
| [Clang](http://clang.llvm.org/) | 14 |
|
||||
| [LLVM](http://llvm.org/) | 15 |
|
||||
| [Clang](http://clang.llvm.org/) | 15 |
|
||||
| [Intel XED](https://software.intel.com/en-us/articles/xed-x86-encoder-decoder-software-library) | Latest |
|
||||
| [Python](https://www.python.org/) | 2.7 |
|
||||
| Unzip | Latest |
|
||||
@@ -55,7 +55,7 @@ Remill now comes with a Dockerfile for easier testing. This Dockerfile reference
|
||||
|
||||
The Dockerfile allows for quick builds of multiple supported LLVM, architecture, and Linux configurations.
|
||||
|
||||
Quickstart (builds Remill against LLVM 14 on Ubuntu 20.04 for AMD64):
|
||||
Quickstart (builds Remill against LLVM 15 on Ubuntu 20.04 for AMD64):
|
||||
|
||||
Clone Remill:
|
||||
```shell
|
||||
@@ -71,7 +71,7 @@ docker build . -t remill \
|
||||
-f Dockerfile \
|
||||
--build-arg UBUNTU_VERSION=20.04 \
|
||||
--build-arg ARCH=amd64 \
|
||||
--build-arg LLVM_VERSION=14
|
||||
--build-arg LLVM_VERSION=15
|
||||
```
|
||||
|
||||
Ensure remill works:
|
||||
|
||||
@@ -107,9 +107,6 @@ class DifferentialModuleBuilder {
|
||||
// it is expected that compatible arches share a semantics module.
|
||||
std::unique_ptr<llvm::LLVMContext> context =
|
||||
std::make_unique<llvm::LLVMContext>();
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
context->enableOpaquePointers();
|
||||
#endif
|
||||
auto tmp_arch = remill::Arch::Build(context.get(), os_name_1, arch_name_1);
|
||||
std::shared_ptr<llvm::Module> semantics_module =
|
||||
remill::LoadArchSemantics(tmp_arch.get());
|
||||
@@ -142,7 +139,7 @@ class DifferentialModuleBuilder {
|
||||
auto f2 = f2_and_name.first;
|
||||
|
||||
for (auto x : {f1, f2}) {
|
||||
llvm::verifyFunction(*x, &llvm::errs());
|
||||
CHECK(remill::VerifyFunction(x));
|
||||
}
|
||||
|
||||
|
||||
|
||||
+8
-4
@@ -16,6 +16,8 @@
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/Function.h>
|
||||
#include <llvm/IR/GlobalValue.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
@@ -23,6 +25,7 @@
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/IR/Type.h>
|
||||
#include <llvm/IR/Verifier.h>
|
||||
#include <llvm/Support/FileSystem.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <remill/Arch/Arch.h>
|
||||
@@ -241,9 +244,6 @@ int main(int argc, char *argv[]) {
|
||||
// Make sure `--address` and `--entry_address` are in-bounds for the target
|
||||
// architecture's address size.
|
||||
llvm::LLVMContext context;
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
context.enableOpaquePointers();
|
||||
#endif
|
||||
auto arch = remill::Arch::Get(context, FLAGS_os, FLAGS_arch);
|
||||
const uint64_t addr_mask = ~0ULL >> (64UL - arch->address_size);
|
||||
if (FLAGS_address != (FLAGS_address & addr_mask)) {
|
||||
@@ -395,7 +395,9 @@ int main(int argc, char *argv[]) {
|
||||
llvm::Value *trace_args[remill::kNumBlockArgs] = {};
|
||||
trace_args[remill::kStatePointerArgNum] = state_ptr;
|
||||
trace_args[remill::kMemoryPointerArgNum] = mem_ptr;
|
||||
trace_args[remill::kPCArgNum] = trace_pc;
|
||||
trace_args[remill::kPCArgNum] = llvm::ConstantInt::get(
|
||||
llvm::IntegerType::get(context, arch->address_size),
|
||||
FLAGS_entry_address, false);
|
||||
|
||||
mem_ptr = ir.CreateCall(entry_trace, trace_args);
|
||||
|
||||
@@ -432,6 +434,8 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
guide.slp_vectorize = true;
|
||||
guide.loop_vectorize = true;
|
||||
|
||||
CHECK(remill::VerifyModule(&dest_module));
|
||||
remill::OptimizeBareModule(&dest_module, guide);
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -28,5 +28,6 @@ option(REMILL_ENABLE_INSTALL_TARGET "Should Remill be installed?" TRUE)
|
||||
cmake_dependent_option(REMILL_ENABLE_TESTING "Build your tests" ON "can_enable_testing" OFF)
|
||||
cmake_dependent_option(REMILL_ENABLE_TESTING_X86 "Build your tests" ON "REMILL_ENABLE_TESTING;can_enable_testing_x86" OFF)
|
||||
cmake_dependent_option(REMILL_ENABLE_TESTING_AARCH64 "Build your tests" ON "REMILL_ENABLE_TESTING;can_enable_testing_aarch64" OFF)
|
||||
cmake_dependent_option(REMILL_ENABLE_TESTING_SLEIGH_THUMB "Build cross platform sliegh tests" ON "REMILL_ENABLE_TESTING" OFF)
|
||||
cmake_dependent_option(REMILL_ENABLE_DIFFERENTIAL_TESTING "Build cross platform differential testing of sleigh x86" ON "REMILL_ENABLE_TESTING" OFF)
|
||||
cmake_dependent_option(REMILL_ENABLE_TESTING_SLEIGH_THUMB "Build cross platform sleigh tests thumb" ON "REMILL_ENABLE_TESTING" OFF)
|
||||
cmake_dependent_option(REMILL_ENABLE_TESTING_SLEIGH_PPC "Build cross platform sliegh tests for ppc" ON "REMILL_ENABLE_TESTING" OFF)
|
||||
cmake_dependent_option(REMILL_ENABLE_DIFFERENTIAL_TESTING "Build cross platform differential testing of sleigh x86" ON "REMILL_ENABLE_TESTING" OFF)
|
||||
|
||||
@@ -29,7 +29,7 @@ macro(main)
|
||||
#
|
||||
|
||||
# Globally set the required C++ standard
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
if(UNIX)
|
||||
|
||||
@@ -356,6 +356,7 @@ class Arch {
|
||||
bool IsAArch64(void) const;
|
||||
bool IsSPARC32(void) const;
|
||||
bool IsSPARC64(void) const;
|
||||
bool IsPPC(void) const;
|
||||
|
||||
bool IsWindows(void) const;
|
||||
bool IsLinux(void) const;
|
||||
@@ -420,14 +421,18 @@ class Arch {
|
||||
static ArchPtr GetAArch64(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
// Defined in `lib/Arch/SleighX86/Arch.cpp`
|
||||
// Defined in `lib/Arch/Sleigh/X86Arch.cpp`
|
||||
static ArchPtr GetSleighX86(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
// Defined in `lib/Arch/SleighThumb2/Arch.cpp`
|
||||
// Defined in `lib/Arch/Sleigh/Thumb2Arch.cpp`
|
||||
static ArchPtr GetSleighThumb2(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
// Defined in `lib/Arch/Sleigh/PPCArch.cpp`
|
||||
static ArchPtr GetSleighPPC(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
// Defined in `lib/Arch/SPARC32/Arch.cpp`.
|
||||
static ArchPtr GetSPARC(llvm::LLVMContext *context, OSName os,
|
||||
ArchName arch_name);
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
namespace remill {
|
||||
|
||||
using ContextValues = std::map<std::string, uint64_t>;
|
||||
|
||||
/// A decoding context is contextual information about the state of the program that affects decoding, ie. the thumb mode register on ARM
|
||||
/// We allow clients to interpose on a context for resolution
|
||||
|
||||
@@ -36,14 +38,14 @@ namespace remill {
|
||||
class DecodingContext {
|
||||
|
||||
private:
|
||||
std::map<std::string, uint64_t> context_value;
|
||||
ContextValues context_value;
|
||||
|
||||
public:
|
||||
bool operator==(const DecodingContext &rhs) const;
|
||||
|
||||
DecodingContext() = default;
|
||||
|
||||
DecodingContext(std::map<std::string, uint64_t> context_value);
|
||||
DecodingContext(ContextValues context_value);
|
||||
|
||||
|
||||
void UpdateContextReg(std::string creg, uint64_t value);
|
||||
@@ -55,6 +57,8 @@ class DecodingContext {
|
||||
uint64_t GetContextValue(const std::string &context_reg) const;
|
||||
DecodingContext PutContextReg(std::string creg, uint64_t value) const;
|
||||
DecodingContext ContextWithoutRegister(const std::string &creg) const;
|
||||
|
||||
const ContextValues &GetContextValues() const;
|
||||
};
|
||||
|
||||
} // namespace remill
|
||||
} // namespace remill
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
# define REMILL_ON_AARCH32 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# define REMILL_ON_PPC 0
|
||||
# elif defined(__i386__) || defined(_M_X86)
|
||||
# define REMILL_ARCH "x86"
|
||||
# define REMILL_ON_AMD64 0
|
||||
@@ -33,6 +34,7 @@
|
||||
# define REMILL_ON_AARCH32 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# define REMILL_ON_PPC 0
|
||||
# elif defined(__aarch64__)
|
||||
# define REMILL_ARCH "aarch64"
|
||||
# define REMILL_ON_AMD64 0
|
||||
@@ -41,6 +43,7 @@
|
||||
# define REMILL_ON_AARCH32 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# define REMILL_ON_PPC 0
|
||||
# elif defined(__arm__)
|
||||
# define REMILL_ARCH "aarch32"
|
||||
# define REMILL_ON_AMD64 0
|
||||
@@ -49,12 +52,14 @@
|
||||
# define REMILL_ON_AARCH32 1
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# define REMILL_ON_PPC 0
|
||||
# elif defined(__sparc__) || defined(__sparc) || defined(__sparc_v8__) || \
|
||||
defined(__sparc_v9__) || defined(__sparcv8) || defined(__sparcv9)
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# define REMILL_ON_AARCH32 0
|
||||
# define REMILL_ON_PPC 0
|
||||
# if (defined(__LP64__) && __LP64__) || (defined(_LP64) && _LP64)
|
||||
# define REMILL_ARCH "sparc64"
|
||||
# define REMILL_ON_SPARC64 1
|
||||
@@ -64,6 +69,15 @@
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 1
|
||||
# endif
|
||||
# elif defined(__PPC__)
|
||||
# define REMILL_ARCH "ppc"
|
||||
# define REMILL_ON_AMD64 0
|
||||
# define REMILL_ON_X86 0
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# define REMILL_ON_AARCH32 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# define REMILL_ON_PPC 1
|
||||
# else
|
||||
# error "Cannot infer current architecture."
|
||||
# define REMILL_ARCH "invalid"
|
||||
@@ -72,6 +86,7 @@
|
||||
# define REMILL_ON_AARCH64 0
|
||||
# define REMILL_ON_SPARC64 0
|
||||
# define REMILL_ON_SPARC32 0
|
||||
# define REMILL_ON_PPC 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -102,6 +117,8 @@ enum ArchName : uint32_t {
|
||||
kArchSparc64,
|
||||
|
||||
kArchThumb2LittleEndian,
|
||||
|
||||
kArchPPC,
|
||||
};
|
||||
|
||||
ArchName GetArchName(const llvm::Triple &triple);
|
||||
|
||||
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic fatal "-Wpadded"
|
||||
|
||||
#include "remill/Arch/Runtime/State.h"
|
||||
|
||||
#if !defined(INCLUDED_FROM_REMILL)
|
||||
# include "remill/Arch/Runtime/Types.h"
|
||||
#endif
|
||||
|
||||
struct Reg final {
|
||||
union {
|
||||
alignas(4) uint32_t dword;
|
||||
alignas(8) uint64_t qword;
|
||||
} __attribute__((packed));
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(uint64_t) == sizeof(Reg), "Invalid packing of `Reg`.");
|
||||
static_assert(0 == __builtin_offsetof(Reg, dword),
|
||||
"Invalid packing of `Reg::dword`.");
|
||||
|
||||
static_assert(0 == __builtin_offsetof(Reg, qword),
|
||||
"Invalid packing of `Reg::qword`.");
|
||||
|
||||
// General Purpose Registers
|
||||
struct alignas(8) GPR final {
|
||||
|
||||
// Prevents LLVM from casting a `GPR` into an `i64` to access `r0`.
|
||||
volatile uint64_t _0;
|
||||
Reg r0;
|
||||
volatile uint64_t _1;
|
||||
Reg r1;
|
||||
volatile uint64_t _2;
|
||||
Reg r2;
|
||||
volatile uint64_t _3;
|
||||
Reg r3;
|
||||
volatile uint64_t _4;
|
||||
Reg r4;
|
||||
volatile uint64_t _5;
|
||||
Reg r5;
|
||||
volatile uint64_t _6;
|
||||
Reg r6;
|
||||
volatile uint64_t _7;
|
||||
Reg r7;
|
||||
volatile uint64_t _8;
|
||||
Reg r8;
|
||||
volatile uint64_t _9;
|
||||
Reg r9;
|
||||
volatile uint64_t _10;
|
||||
Reg r10;
|
||||
volatile uint64_t _11;
|
||||
Reg r11;
|
||||
volatile uint64_t _12;
|
||||
Reg r12;
|
||||
volatile uint64_t _13;
|
||||
Reg r13;
|
||||
volatile uint64_t _14;
|
||||
Reg r14;
|
||||
volatile uint64_t _15;
|
||||
Reg r15;
|
||||
volatile uint64_t _16;
|
||||
Reg r16;
|
||||
volatile uint64_t _17;
|
||||
Reg r17;
|
||||
volatile uint64_t _18;
|
||||
Reg r18;
|
||||
volatile uint64_t _19;
|
||||
Reg r19;
|
||||
volatile uint64_t _20;
|
||||
Reg r20;
|
||||
volatile uint64_t _21;
|
||||
Reg r21;
|
||||
volatile uint64_t _22;
|
||||
Reg r22;
|
||||
volatile uint64_t _23;
|
||||
Reg r23;
|
||||
volatile uint64_t _24;
|
||||
Reg r24;
|
||||
volatile uint64_t _25;
|
||||
Reg r25;
|
||||
volatile uint64_t _26;
|
||||
Reg r26;
|
||||
volatile uint64_t _27;
|
||||
Reg r27;
|
||||
volatile uint64_t _28;
|
||||
Reg r28;
|
||||
volatile uint64_t _29;
|
||||
Reg r29;
|
||||
volatile uint64_t _30;
|
||||
Reg r30;
|
||||
volatile uint64_t _31;
|
||||
Reg r31;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(512 == sizeof(GPR), "Invalid structure packing of `GPR`.");
|
||||
|
||||
// Floating Pointer Registers
|
||||
struct alignas(8) FPR final {
|
||||
|
||||
// Prevents LLVM from casting an `FPR` into an `i64` to access `f0`.
|
||||
volatile uint64_t _0;
|
||||
Reg f0;
|
||||
volatile uint64_t _1;
|
||||
Reg f1;
|
||||
volatile uint64_t _2;
|
||||
Reg f2;
|
||||
volatile uint64_t _3;
|
||||
Reg f3;
|
||||
volatile uint64_t _4;
|
||||
Reg f4;
|
||||
volatile uint64_t _5;
|
||||
Reg f5;
|
||||
volatile uint64_t _6;
|
||||
Reg f6;
|
||||
volatile uint64_t _7;
|
||||
Reg f7;
|
||||
volatile uint64_t _8;
|
||||
Reg f8;
|
||||
volatile uint64_t _9;
|
||||
Reg f9;
|
||||
volatile uint64_t _10;
|
||||
Reg f10;
|
||||
volatile uint64_t _11;
|
||||
Reg f11;
|
||||
volatile uint64_t _12;
|
||||
Reg f12;
|
||||
volatile uint64_t _13;
|
||||
Reg f13;
|
||||
volatile uint64_t _14;
|
||||
Reg f14;
|
||||
volatile uint64_t _15;
|
||||
Reg f15;
|
||||
volatile uint64_t _16;
|
||||
Reg f16;
|
||||
volatile uint64_t _17;
|
||||
Reg f17;
|
||||
volatile uint64_t _18;
|
||||
Reg f18;
|
||||
volatile uint64_t _19;
|
||||
Reg f19;
|
||||
volatile uint64_t _20;
|
||||
Reg f20;
|
||||
volatile uint64_t _21;
|
||||
Reg f21;
|
||||
volatile uint64_t _22;
|
||||
Reg f22;
|
||||
volatile uint64_t _23;
|
||||
Reg f23;
|
||||
volatile uint64_t _24;
|
||||
Reg f24;
|
||||
volatile uint64_t _25;
|
||||
Reg f25;
|
||||
volatile uint64_t _26;
|
||||
Reg f26;
|
||||
volatile uint64_t _27;
|
||||
Reg f27;
|
||||
volatile uint64_t _28;
|
||||
Reg f28;
|
||||
volatile uint64_t _29;
|
||||
Reg f29;
|
||||
volatile uint64_t _30;
|
||||
Reg f30;
|
||||
volatile uint64_t _31;
|
||||
Reg f31;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(512 == sizeof(FPR), "Invalid structure packing of `FPR`.");
|
||||
|
||||
// These are actually bitflags.
|
||||
//
|
||||
// Remill's notion of registers operates at a byte granularity so these need to
|
||||
// take a full byte.
|
||||
struct alignas(8) CRFlags final {
|
||||
|
||||
volatile uint8_t _0;
|
||||
uint8_t cr0;
|
||||
volatile uint8_t _1;
|
||||
uint8_t cr1;
|
||||
volatile uint8_t _2;
|
||||
uint8_t cr2;
|
||||
volatile uint8_t _3;
|
||||
uint8_t cr3;
|
||||
volatile uint8_t _4;
|
||||
uint8_t cr4;
|
||||
volatile uint8_t _5;
|
||||
uint8_t cr5;
|
||||
volatile uint8_t _6;
|
||||
uint8_t cr6;
|
||||
volatile uint8_t _7;
|
||||
uint8_t cr7;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
struct alignas(8) XERFlags final {
|
||||
|
||||
volatile uint8_t _0;
|
||||
uint8_t so;
|
||||
volatile uint8_t _1;
|
||||
uint8_t ov;
|
||||
volatile uint8_t _2;
|
||||
uint8_t ca;
|
||||
volatile uint8_t _3;
|
||||
uint8_t sl;
|
||||
// uint8_t _padding[2];
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
// Instruction-Accessible Registers
|
||||
struct alignas(8) IAR final {
|
||||
|
||||
// Prevents LLVM from casting an `IAR` into an `i64` to access `cr`.
|
||||
volatile uint64_t _1;
|
||||
Reg cr;
|
||||
volatile uint64_t _2;
|
||||
Reg ctr;
|
||||
volatile uint64_t _3;
|
||||
Reg lr;
|
||||
volatile uint64_t _4;
|
||||
Reg xer;
|
||||
volatile uint64_t _5;
|
||||
Reg spefscr;
|
||||
volatile uint64_t _6;
|
||||
Reg acc;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
// Read-Only Performance Monitor Registers
|
||||
struct alignas(8) UPM final {
|
||||
|
||||
volatile uint64_t _0;
|
||||
Reg gc;
|
||||
|
||||
// Counter registers
|
||||
volatile uint64_t _1;
|
||||
Reg c0;
|
||||
volatile uint64_t _2;
|
||||
Reg c1;
|
||||
volatile uint64_t _3;
|
||||
Reg c2;
|
||||
volatile uint64_t _4;
|
||||
Reg c3;
|
||||
|
||||
// Local control registers
|
||||
volatile uint64_t _5;
|
||||
Reg lca0;
|
||||
volatile uint64_t _6;
|
||||
Reg lca1;
|
||||
volatile uint64_t _7;
|
||||
Reg lca2;
|
||||
volatile uint64_t _8;
|
||||
Reg lca3;
|
||||
volatile uint64_t _9;
|
||||
Reg lcb0;
|
||||
volatile uint64_t _10;
|
||||
Reg lcb1;
|
||||
volatile uint64_t _11;
|
||||
Reg lcb2;
|
||||
volatile uint64_t _12;
|
||||
Reg lcb3;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
// Time-Based Registers
|
||||
struct alignas(8) TBR final {
|
||||
|
||||
volatile uint64_t _0;
|
||||
Reg tbl;
|
||||
volatile uint64_t _1;
|
||||
Reg tbu;
|
||||
volatile uint64_t _2;
|
||||
Reg atbl;
|
||||
volatile uint64_t _3;
|
||||
Reg atbu;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
// General Special-Purpose Registers
|
||||
struct alignas(8) SPRG final {
|
||||
|
||||
volatile uint64_t _0;
|
||||
Reg r3;
|
||||
volatile uint64_t _1;
|
||||
Reg r4;
|
||||
volatile uint64_t _2;
|
||||
Reg r5;
|
||||
volatile uint64_t _3;
|
||||
Reg r6;
|
||||
volatile uint64_t _4;
|
||||
Reg r7;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
// L1 Cache Configuration
|
||||
struct alignas(8) L1CFG final {
|
||||
|
||||
volatile uint64_t _0;
|
||||
Reg r0;
|
||||
volatile uint64_t _1;
|
||||
Reg r1;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
// Signals
|
||||
//
|
||||
// These are signals that are commonly found on PPC devices
|
||||
// They are implemented as registers here as Sleigh treats them that way
|
||||
struct alignas(8) Signals final {
|
||||
volatile uint64_t _0;
|
||||
Reg tea; // Transfer Error Acknowledge
|
||||
uint8_t _padding[8];
|
||||
};
|
||||
|
||||
struct alignas(8) PPCState : public ArchState {
|
||||
|
||||
GPR gpr; // 528 bytes.
|
||||
|
||||
uint64_t _0;
|
||||
|
||||
FPR fpr;
|
||||
|
||||
uint64_t _1;
|
||||
|
||||
IAR iar;
|
||||
|
||||
uint64_t _2;
|
||||
|
||||
UPM upm;
|
||||
|
||||
uint64_t _3;
|
||||
|
||||
TBR tbr;
|
||||
|
||||
uint64_t _4;
|
||||
|
||||
Reg uspr; // User Special-Purpose Register
|
||||
|
||||
uint64_t _5;
|
||||
|
||||
SPRG sprg;
|
||||
|
||||
uint64_t _6;
|
||||
|
||||
L1CFG l1cfg;
|
||||
|
||||
uint64_t _7;
|
||||
|
||||
Reg pc; // This isn't exposed via PPC's API however Sleigh uses a "fake" register to maintain the program counter
|
||||
|
||||
uint64_t _8;
|
||||
|
||||
XERFlags xer_flags;
|
||||
|
||||
uint64_t _9;
|
||||
|
||||
CRFlags cr_flags;
|
||||
|
||||
uint64_t _10;
|
||||
|
||||
Signals signals;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
// static_assert((1152 + 16) == sizeof(PPCState),
|
||||
// "Invalid packing of `struct State`");
|
||||
|
||||
struct State : public PPCState {};
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
@@ -93,6 +93,8 @@ class SyncHyperCall {
|
||||
kSPARCTrapCondNEG,
|
||||
kSPARCTrapCondVC,
|
||||
kSPARCTrapCondVS,
|
||||
|
||||
kPPCEmulateInstruction,
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#if __has_include(<cstdint>)
|
||||
# include <cstdint>
|
||||
# include <cstddef>
|
||||
#elif __has_include(<cinttypes>)
|
||||
# include <cinttypes>
|
||||
#else
|
||||
@@ -64,7 +65,7 @@ using uint64_t = TypeSelector<8, unsigned, unsigned long, unsigned long long>::T
|
||||
#endif // cstint, cinttypes
|
||||
|
||||
#if !defined(REMILL_DISABLE_INT128)
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X86) || defined (__arm__)
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X86) || defined (__arm__) || defined(__PPC__)
|
||||
typedef unsigned uint128_t __attribute__((mode(TI)));
|
||||
typedef int int128_t __attribute__((mode(TI)));
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
@@ -420,4 +420,7 @@ __remill_sparc32_emulate_instruction(Memory *);
|
||||
[[gnu::used, gnu::const]] extern Memory *
|
||||
__remill_sparc64_emulate_instruction(Memory *);
|
||||
|
||||
[[gnu::used, gnu::const]] extern Memory *
|
||||
__remill_ppc_emulate_instruction(Memory *);
|
||||
|
||||
} // extern C
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <sleigh/libsleigh.hh>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/Arch/Sleigh/ControlFlowStructuring.h"
|
||||
|
||||
|
||||
namespace remill::sleigh {
|
||||
|
||||
// A zero-indexed pointer to a PcodeOp within an instruction, relative
|
||||
// to the first PcodeOp of the instruction.
|
||||
struct IntrainstructionIndex {
|
||||
size_t target_block_index;
|
||||
};
|
||||
|
||||
struct InstrExit {};
|
||||
|
||||
using Exit = std::variant<IntrainstructionIndex, InstrExit>;
|
||||
|
||||
struct ConditionalExit {
|
||||
Exit true_branch;
|
||||
Exit false_branch;
|
||||
};
|
||||
|
||||
using BlockExit = std::variant<Exit, ConditionalExit>;
|
||||
|
||||
class PcodeBlock {
|
||||
public:
|
||||
size_t base_index;
|
||||
std::vector<RemillPcodeOp> ops;
|
||||
BlockExit block_exit;
|
||||
|
||||
// Default block with an instruction exit and no ops.
|
||||
PcodeBlock(size_t base_index);
|
||||
|
||||
PcodeBlock(size_t base_index, std::vector<RemillPcodeOp> ops,
|
||||
BlockExit block_exit);
|
||||
};
|
||||
|
||||
class PcodeCFG {
|
||||
public:
|
||||
std::map<size_t, PcodeBlock> blocks;
|
||||
|
||||
PcodeCFG(std::map<size_t, PcodeBlock> blocks);
|
||||
};
|
||||
|
||||
PcodeCFG CreateCFG(const std::vector<RemillPcodeOp> &linear_ops);
|
||||
|
||||
class PcodeCFGBuilder {
|
||||
public:
|
||||
explicit PcodeCFGBuilder(const std::vector<RemillPcodeOp> &linear_ops);
|
||||
PcodeCFG Build() const;
|
||||
|
||||
private:
|
||||
PcodeBlock BuildBlock(size_t start_ind, size_t next_start) const;
|
||||
BlockExit GetBlockExitsForIndex(size_t index) const;
|
||||
std::vector<size_t> GetIntraProcTargets(size_t index) const;
|
||||
std::vector<size_t> GetBlockStarts() const;
|
||||
|
||||
const std::vector<RemillPcodeOp> &linear_ops;
|
||||
};
|
||||
|
||||
|
||||
} // namespace remill::sleigh
|
||||
@@ -35,6 +35,7 @@ struct BranchTakenVar {
|
||||
VarnodeData target_vnode;
|
||||
size_t index;
|
||||
};
|
||||
using MaybeBranchTakenVar = std::optional<BranchTakenVar>;
|
||||
|
||||
class SleighDecoder;
|
||||
class SingleInstructionSleighContext;
|
||||
@@ -60,9 +61,11 @@ class SleighLifter : public InstructionLifter {
|
||||
|
||||
virtual ~SleighLifter(void) = default;
|
||||
|
||||
LiftStatus LiftIntoBlockWithSleighState(
|
||||
Instruction &inst, llvm::BasicBlock *block, llvm::Value *state_ptr,
|
||||
bool is_delayed, const std::optional<sleigh::BranchTakenVar> &btaken);
|
||||
LiftStatus
|
||||
LiftIntoBlockWithSleighState(Instruction &inst, llvm::BasicBlock *block,
|
||||
llvm::Value *state_ptr, bool is_delayed,
|
||||
const sleigh::MaybeBranchTakenVar &btaken,
|
||||
const ContextValues &context_values);
|
||||
|
||||
private:
|
||||
static void SetISelAttributes(llvm::Function *);
|
||||
@@ -74,7 +77,8 @@ class SleighLifter : public InstructionLifter {
|
||||
std::pair<LiftStatus, std::optional<llvm::Function *>>
|
||||
LiftIntoInternalBlockWithSleighState(
|
||||
Instruction &inst, llvm::Module *target_mod, bool is_delayed,
|
||||
const std::optional<sleigh::BranchTakenVar> &btaken);
|
||||
const sleigh::MaybeBranchTakenVar &btaken,
|
||||
const ContextValues &context_values);
|
||||
|
||||
::Sleigh &GetEngine(void) const;
|
||||
};
|
||||
@@ -83,11 +87,13 @@ class SleighLifter : public InstructionLifter {
|
||||
// lets us attach state to a lifter that we need to carry on from when we decoded the instruction
|
||||
class SleighLifterWithState final : public InstructionLifterIntf {
|
||||
private:
|
||||
std::optional<sleigh::BranchTakenVar> btaken;
|
||||
sleigh::MaybeBranchTakenVar btaken;
|
||||
ContextValues context_values;
|
||||
std::shared_ptr<SleighLifter> lifter;
|
||||
|
||||
public:
|
||||
SleighLifterWithState(std::optional<sleigh::BranchTakenVar> btaken,
|
||||
SleighLifterWithState(sleigh::MaybeBranchTakenVar btaken,
|
||||
ContextValues context_values,
|
||||
std::shared_ptr<SleighLifter> lifter_);
|
||||
|
||||
// Lift a single instruction into a basic block. `is_delayed` signifies that
|
||||
@@ -110,6 +116,10 @@ class SleighLifterWithState final : public InstructionLifterIntf {
|
||||
virtual llvm::Type *GetMemoryType() override;
|
||||
|
||||
virtual void ClearCache(void) const override;
|
||||
|
||||
const ContextValues &GetContextValues() const {
|
||||
return context_values;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace remill
|
||||
|
||||
@@ -165,6 +165,11 @@ bool VerifyModule(llvm::Module *module);
|
||||
// Returns diagnostic message if verify failed.
|
||||
std::optional<std::string> VerifyModuleMsg(llvm::Module *module);
|
||||
|
||||
// Try to verify a function.
|
||||
bool VerifyFunction(llvm::Function *func);
|
||||
// Returns diagnostic message if verify failed.
|
||||
std::optional<std::string> VerifyFunctionMsg(llvm::Function *func);
|
||||
|
||||
|
||||
std::unique_ptr<llvm::Module>
|
||||
LoadModuleFromFile(llvm::LLVMContext *context, std::filesystem::path file_name);
|
||||
|
||||
@@ -29,25 +29,16 @@
|
||||
|
||||
#define LLVM_VERSION_NUMBER LLVM_VERSION(LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR)
|
||||
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(14, 0)
|
||||
# error "Minimum supported LLVM version is 14.0"
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
# error "Minimum supported LLVM version is 15.0"
|
||||
#endif
|
||||
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
# define IF_LLVM_LT_1500(...) __VA_ARGS__
|
||||
# define IF_LLVM_LT_1500_(...) __VA_ARGS__,
|
||||
# define _IF_LLVM_LT_1500(...) , __VA_ARGS__
|
||||
# define IF_LLVM_GTE_1500(...)
|
||||
# define IF_LLVM_GTE_1500_(...)
|
||||
# define _IF_LLVM_GTE_1500(...)
|
||||
#else
|
||||
# define IF_LLVM_LT_1500(...)
|
||||
# define IF_LLVM_LT_1500_(...)
|
||||
# define _IF_LLVM_LT_1500(...)
|
||||
# define IF_LLVM_GTE_1500(...) __VA_ARGS__
|
||||
# define IF_LLVM_GTE_1500_(...) __VA_ARGS__,
|
||||
# define _IF_LLVM_GTE_1500(...) , __VA_ARGS__
|
||||
#endif
|
||||
#define IF_LLVM_LT_1500(...)
|
||||
#define IF_LLVM_LT_1500_(...)
|
||||
#define _IF_LLVM_LT_1500(...)
|
||||
#define IF_LLVM_GTE_1500(...) __VA_ARGS__
|
||||
#define IF_LLVM_GTE_1500_(...) __VA_ARGS__,
|
||||
#define _IF_LLVM_GTE_1500(...) , __VA_ARGS__
|
||||
|
||||
#define IF_LLVM_LT(major, minor, ...) IF_LLVM_LT_##major##minor##0(__VA_ARGS__)
|
||||
|
||||
|
||||
+14
-2
@@ -53,7 +53,8 @@ static unsigned AddressSize(ArchName arch_name) {
|
||||
case kArchX86_SLEIGH:
|
||||
case kArchAArch32LittleEndian:
|
||||
case kArchThumb2LittleEndian:
|
||||
case kArchSparc32: return 32;
|
||||
case kArchSparc32:
|
||||
case kArchPPC: return 32;
|
||||
case kArchAMD64:
|
||||
case kArchAMD64_AVX:
|
||||
case kArchAMD64_AVX512:
|
||||
@@ -107,7 +108,8 @@ ArchLocker Arch::Lock(ArchName arch_name_) {
|
||||
case ArchName::kArchAArch32LittleEndian:
|
||||
case ArchName::kArchThumb2LittleEndian:
|
||||
case ArchName::kArchAMD64_SLEIGH:
|
||||
case ArchName::kArchX86_SLEIGH: return &gSleighArchLock;
|
||||
case ArchName::kArchX86_SLEIGH:
|
||||
case ArchName::kArchPPC: return &gSleighArchLock;
|
||||
default: return ArchLocker();
|
||||
}
|
||||
}
|
||||
@@ -221,6 +223,12 @@ auto Arch::GetArchByName(llvm::LLVMContext *context_, OSName os_name_,
|
||||
DLOG(INFO) << "Using architecture: 64-bit SPARC";
|
||||
return GetSPARC64(context_, os_name_, arch_name_);
|
||||
}
|
||||
|
||||
case kArchPPC: {
|
||||
DLOG(INFO) << "Using architecture: PowerPC";
|
||||
return GetSleighPPC(context_, os_name_, arch_name_);
|
||||
}
|
||||
|
||||
default: {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -398,6 +406,10 @@ bool Arch::IsSPARC64(void) const {
|
||||
return remill::kArchSparc64 == arch_name;
|
||||
}
|
||||
|
||||
bool Arch::IsPPC(void) const {
|
||||
return remill::kArchPPC == arch_name;
|
||||
}
|
||||
|
||||
bool Arch::IsWindows(void) const {
|
||||
return remill::kOSWindows == os_name;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ add_library(remill_arch STATIC
|
||||
|
||||
add_subdirectory(AArch32)
|
||||
add_subdirectory(AArch64)
|
||||
add_subdirectory(PPC)
|
||||
add_subdirectory(SPARC32)
|
||||
add_subdirectory(SPARC64)
|
||||
add_subdirectory(Sleigh)
|
||||
|
||||
@@ -9,7 +9,7 @@ bool DecodingContext::operator==(remill::DecodingContext const &rhs) const {
|
||||
return this->context_value == rhs.context_value;
|
||||
}
|
||||
|
||||
DecodingContext::DecodingContext(std::map<std::string, uint64_t> context_value)
|
||||
DecodingContext::DecodingContext(ContextValues context_value)
|
||||
: context_value(std::move(context_value)) {}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ DecodingContext::GetContextValue(const std::string &context_reg) const {
|
||||
|
||||
DecodingContext DecodingContext::PutContextReg(std::string creg,
|
||||
uint64_t value) const {
|
||||
std::map<std::string, uint64_t> new_value(this->context_value);
|
||||
auto new_value = context_value;
|
||||
new_value.emplace(creg, value);
|
||||
return DecodingContext(std::move(new_value));
|
||||
}
|
||||
@@ -50,4 +50,8 @@ DecodingContext::ContextWithoutRegister(const std::string &creg) const {
|
||||
return cpy;
|
||||
}
|
||||
|
||||
} // namespace remill
|
||||
const ContextValues &DecodingContext::GetContextValues() const {
|
||||
return this->context_value;
|
||||
}
|
||||
|
||||
} // namespace remill
|
||||
|
||||
@@ -670,6 +670,7 @@ std::string Instruction::Serialize(void) const {
|
||||
case kArchAArch64LittleEndian: ss << "AArch64"; break;
|
||||
case kArchSparc32: ss << "SPARC32"; break;
|
||||
case kArchSparc64: ss << "SPARC64"; break;
|
||||
case kArchPPC: ss << "PowerPC"; break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ ArchName GetArchName(const llvm::Triple &triple) {
|
||||
case llvm::Triple::ArchType::thumb: return kArchThumb2LittleEndian;
|
||||
case llvm::Triple::sparc: return kArchSparc32;
|
||||
case llvm::Triple::sparcv9: return kArchSparc64;
|
||||
case llvm::Triple::ppc: return kArchPPC;
|
||||
default: return kArchInvalid;
|
||||
}
|
||||
}
|
||||
@@ -73,6 +74,9 @@ ArchName GetArchName(std::string_view arch_name) {
|
||||
} else if (arch_name == "sparc64") {
|
||||
return kArchSparc64;
|
||||
|
||||
} else if (arch_name == "ppc") {
|
||||
return kArchPPC;
|
||||
|
||||
} else {
|
||||
return kArchInvalid;
|
||||
}
|
||||
@@ -95,6 +99,7 @@ static const std::string_view kArchNames[] = {
|
||||
[kArchAArch64LittleEndian] = "aarch64",
|
||||
[kArchSparc32] = "sparc32",
|
||||
[kArchSparc64] = "sparc64",
|
||||
[kArchPPC] = "ppc",
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(Runtime)
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright (c) 2022-present Trail of Bits, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(ppc_runtime)
|
||||
|
||||
set(PPCRUNTIME_SOURCEFILES
|
||||
Instructions.cpp
|
||||
|
||||
"${REMILL_LIB_DIR}/Arch/Runtime/Intrinsics.cpp"
|
||||
)
|
||||
|
||||
set_source_files_properties(BasicBlock.cpp PROPERTIES COMPILE_FLAGS "-O3 -g0")
|
||||
|
||||
# Visual C++ requires C++14
|
||||
if(WIN32)
|
||||
set(required_cpp_standard "c++14")
|
||||
else()
|
||||
set(required_cpp_standard "c++17")
|
||||
endif()
|
||||
|
||||
add_runtime(ppc
|
||||
SOURCES ${PPCRUNTIME_SOURCEFILES}
|
||||
ADDRESS_SIZE 32
|
||||
DEFINITIONS "LITTLE_ENDIAN=${little_endian}"
|
||||
BCFLAGS "-std=${required_cpp_standard}"
|
||||
INCLUDEDIRECTORIES "${REMILL_INCLUDE_DIR}" "${REMILL_SOURCE_DIR}"
|
||||
INSTALLDESTINATION "${REMILL_INSTALL_SEMANTICS_DIR}"
|
||||
ARCH ppc
|
||||
|
||||
DEPENDENCIES
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/PPC/Runtime/State.h"
|
||||
)
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "remill/Arch/PPC/Runtime/State.h"
|
||||
#include "remill/Arch/Runtime/Float.h"
|
||||
#include "remill/Arch/Runtime/Intrinsics.h"
|
||||
#include "remill/Arch/Runtime/Operators.h"
|
||||
|
||||
// A definition is required to ensure that LLVM doesn't optimize the `State` type out of the bytecode
|
||||
// See https://github.com/lifting-bits/remill/pull/631#issuecomment-1279989004f
|
||||
State __remill_state;
|
||||
|
||||
#define HYPER_CALL state.hyper_call
|
||||
|
||||
namespace {
|
||||
|
||||
DEF_SEM(HandleUnsupported) {
|
||||
return __remill_sync_hyper_call(state, memory,
|
||||
SyncHyperCall::kPPCEmulateInstruction);
|
||||
}
|
||||
|
||||
DEF_SEM(HandleInvalidInstruction) {
|
||||
HYPER_CALL = AsyncHyperCall::kInvalidInstruction;
|
||||
return memory;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEF_ISEL(UNSUPPORTED_INSTRUCTION) = HandleUnsupported;
|
||||
DEF_ISEL(INVALID_INSTRUCTION) = HandleInvalidInstruction;
|
||||
@@ -36,6 +36,9 @@
|
||||
# else
|
||||
# error "Cannot deduce hyper call SPARC variant"
|
||||
# endif
|
||||
#elif defined(__PPC__)
|
||||
# include "remill/Arch/PPC/Runtime/State.h"
|
||||
# define REMILL_HYPERCALL_PPC 1
|
||||
#else
|
||||
# error "Cannot deduce hyper call architecture"
|
||||
#endif
|
||||
@@ -377,6 +380,12 @@ Memory *__remill_sync_hyper_call(State &state, Memory *mem,
|
||||
|
||||
# endif
|
||||
|
||||
#elif defined(REMILL_HYPERCALL_PPC)
|
||||
|
||||
case SyncHyperCall::kPPCEmulateInstruction:
|
||||
mem = __remill_ppc_emulate_instruction(mem);
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
default: __builtin_unreachable(); break;
|
||||
|
||||
+34
-12
@@ -99,7 +99,7 @@ SingleInstructionSleighContext::SingleInstructionSleighContext(
|
||||
auto pspec_path = ::sleigh::FindSpecFile(pspec_name.c_str());
|
||||
|
||||
if (!pspec_path) {
|
||||
LOG(FATAL) << "Couldn't find required spec file: " << sla_name << '\n';
|
||||
LOG(FATAL) << "Couldn't find required pspec file: " << pspec_name << '\n';
|
||||
}
|
||||
LOG(INFO) << "Using pspec at: " << pspec_path->string();
|
||||
|
||||
@@ -191,6 +191,7 @@ bool SleighDecoder::DecodeInstruction(uint64_t address,
|
||||
DecodingContext context) const {
|
||||
|
||||
|
||||
auto context_values = context.GetContextValues();
|
||||
auto res_cat = const_cast<SleighDecoder *>(this)->DecodeInstructionImpl(
|
||||
address, instr_bytes, inst, std::move(context));
|
||||
|
||||
@@ -201,8 +202,8 @@ bool SleighDecoder::DecodeInstruction(uint64_t address,
|
||||
<< "Should always emit branch taken var for conditional instruction";
|
||||
}
|
||||
|
||||
inst.SetLifter(std::make_shared<SleighLifterWithState>(res_cat->second,
|
||||
this->GetLifter()));
|
||||
inst.SetLifter(std::make_shared<SleighLifterWithState>(
|
||||
res_cat->second, std::move(context_values), this->GetLifter()));
|
||||
CHECK(inst.GetLifter() != nullptr);
|
||||
return res_cat.has_value();
|
||||
}
|
||||
@@ -213,8 +214,8 @@ SleighDecoder::SleighDecoder(
|
||||
std::unordered_map<std::string, std::string> context_reg_map_,
|
||||
std::unordered_map<std::string, std::string> state_reg_map_)
|
||||
: sleigh_ctx(sla_name, pspec_name),
|
||||
sla_name(sla_name),
|
||||
pspec_name(pspec_name),
|
||||
sla_name(std::move(sla_name)),
|
||||
pspec_name(std::move(pspec_name)),
|
||||
lifter(nullptr),
|
||||
arch(arch_),
|
||||
context_reg_mapping(std::move(context_reg_map_)),
|
||||
@@ -231,8 +232,8 @@ SleighDecoder::GetStateRegRemappings() const {
|
||||
return this->state_reg_remappings;
|
||||
}
|
||||
|
||||
std::optional<std::pair<Instruction::InstructionFlowCategory,
|
||||
std::optional<BranchTakenVar>>>
|
||||
std::optional<
|
||||
std::pair<Instruction::InstructionFlowCategory, MaybeBranchTakenVar>>
|
||||
SleighDecoder::DecodeInstructionImpl(uint64_t address,
|
||||
std::string_view instr_bytes,
|
||||
Instruction &inst,
|
||||
@@ -243,7 +244,8 @@ SleighDecoder::DecodeInstructionImpl(uint64_t address,
|
||||
|
||||
// Now decode the instruction.
|
||||
this->sleigh_ctx.resetContext();
|
||||
this->InitializeSleighContext(this->sleigh_ctx);
|
||||
this->InitializeSleighContext(this->sleigh_ctx,
|
||||
curr_context.GetContextValues());
|
||||
PcodeDecoder pcode_handler(this->sleigh_ctx.GetEngine());
|
||||
|
||||
|
||||
@@ -296,12 +298,12 @@ SleighDecoder::DecodeInstructionImpl(uint64_t address,
|
||||
}
|
||||
|
||||
|
||||
std::string SleighDecoder::GetSLAName() const {
|
||||
const std::string &SleighDecoder::GetSLAName() const {
|
||||
return this->sla_name;
|
||||
}
|
||||
|
||||
|
||||
std::string SleighDecoder::GetPSpec() const {
|
||||
const std::string &SleighDecoder::GetPSpec() const {
|
||||
return this->pspec_name;
|
||||
}
|
||||
|
||||
@@ -334,7 +336,7 @@ std::optional<int32_t> SingleInstructionSleighContext::oneInstruction(
|
||||
return std::nullopt;
|
||||
}
|
||||
} catch (BadDataError e) {
|
||||
LOG(ERROR) << "Bad data error";
|
||||
LOG(ERROR) << "Bad data error: " << e.explain;
|
||||
// NOTE (Ian): if sleigh cant find a constructor it throws an exception... yay for unrolling.
|
||||
return std::nullopt;
|
||||
} catch (UnimplError e) {
|
||||
@@ -465,9 +467,29 @@ void SleighDecoder::ApplyFlowToInstruction(remill::Instruction &inst) const {
|
||||
|
||||
std::visit(conditional_applyer, cat.taken_branch);
|
||||
},
|
||||
}; // namespace remill::sleigh
|
||||
};
|
||||
|
||||
std::visit(applyer, inst.flows);
|
||||
}
|
||||
|
||||
uint64_t GetContextRegisterValue(const char *remill_reg_name,
|
||||
uint64_t default_value,
|
||||
const ContextValues &context_values) {
|
||||
const auto iter = context_values.find(remill_reg_name);
|
||||
if (iter != context_values.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
|
||||
void SetContextRegisterValueInSleigh(
|
||||
const char *remill_reg_name, const char *sleigh_reg_name,
|
||||
uint64_t default_value, sleigh::SingleInstructionSleighContext &ctxt,
|
||||
const ContextValues &context_values) {
|
||||
auto value =
|
||||
GetContextRegisterValue(remill_reg_name, default_value, context_values);
|
||||
ctxt.GetContext().setVariableDefault(sleigh_reg_name, value);
|
||||
}
|
||||
|
||||
} // namespace remill::sleigh
|
||||
|
||||
+14
-4
@@ -103,12 +103,12 @@ class SleighDecoder {
|
||||
const remill::Arch &arch, std::string sla_name, std::string pspec_name,
|
||||
std::unordered_map<std::string, std::string> context_reg_mapping,
|
||||
std::unordered_map<std::string, std::string> state_reg_remappings);
|
||||
std::string GetSLAName() const;
|
||||
const std::string &GetSLAName() const;
|
||||
|
||||
std::string GetPSpec() const;
|
||||
const std::string &GetPSpec() const;
|
||||
// Decoder specific prep
|
||||
virtual void
|
||||
InitializeSleighContext(SingleInstructionSleighContext &) const = 0;
|
||||
virtual void InitializeSleighContext(SingleInstructionSleighContext &,
|
||||
const ContextValues &) const = 0;
|
||||
|
||||
|
||||
virtual llvm::Value *LiftPcFromCurrPc(llvm::IRBuilder<> &bldr,
|
||||
@@ -150,4 +150,14 @@ class SleighDecoder {
|
||||
std::unordered_map<std::string, std::string> context_reg_mapping;
|
||||
std::unordered_map<std::string, std::string> state_reg_remappings;
|
||||
};
|
||||
|
||||
uint64_t GetContextRegisterValue(const char *remill_reg_name,
|
||||
uint64_t default_value,
|
||||
const ContextValues &context_values);
|
||||
|
||||
void SetContextRegisterValueInSleigh(
|
||||
const char *remill_reg_name, const char *sleigh_reg_name,
|
||||
uint64_t default_value, sleigh::SingleInstructionSleighContext &ctxt,
|
||||
const ContextValues &context_values);
|
||||
|
||||
} // namespace remill::sleigh
|
||||
|
||||
@@ -30,11 +30,15 @@ add_library(remill_arch_sleigh STATIC
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/AArch32/Runtime/State.h"
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/AArch32/Runtime/Types.h"
|
||||
|
||||
"${REMILL_INCLUDE_DIR}/remill/Arch/PPC/Runtime/State.h"
|
||||
|
||||
Arch.h
|
||||
Thumb.h
|
||||
PPC.h
|
||||
Arch.cpp
|
||||
X86Arch.cpp
|
||||
Thumb2Arch.cpp
|
||||
PPCArch.cpp
|
||||
ControlFlowStructuring.cpp
|
||||
ControlFlowStructuring.h
|
||||
)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include <glog/logging.h>
|
||||
#include <lib/Arch/Sleigh/ControlFlowStructuring.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
namespace remill::sleigh {
|
||||
|
||||
bool isVarnodeInConstantSpace(VarnodeData vnode) {
|
||||
@@ -29,14 +32,19 @@ auto variant_cast(const std::variant<Args...> &v)
|
||||
return {v};
|
||||
}
|
||||
|
||||
enum CoarseEffect { kAbnormal, kNormal };
|
||||
enum class CoarseEffect { kAbnormal, kNormal, kIntraInstruction };
|
||||
|
||||
struct CoarseFlow {
|
||||
CoarseEffect eff;
|
||||
bool is_conditional;
|
||||
};
|
||||
|
||||
enum CoarseCategory { kCatNormal, kCatAbnormal, kCatConditionalAbnormal };
|
||||
enum class CoarseCategory {
|
||||
kCatNormal,
|
||||
kCatNormalWithIntraInstructionFlow,
|
||||
kCatAbnormal,
|
||||
kCatConditionalAbnormal
|
||||
};
|
||||
|
||||
|
||||
static CoarseEffect EffectFromDirectControlFlowOp(const RemillPcodeOp &op,
|
||||
@@ -59,14 +67,13 @@ CoarseFlowFromControlFlowOp(const RemillPcodeOp &op, uint64_t next_pc) {
|
||||
|
||||
auto is_conditional = op.op == CPUI_CBRANCH;
|
||||
if (isVarnodeInConstantSpace(op.vars[0])) {
|
||||
// this is an internal branch.. we cant handle that right now
|
||||
return std::nullopt;
|
||||
return {{CoarseEffect::kIntraInstruction, is_conditional}};
|
||||
}
|
||||
|
||||
return {{EffectFromDirectControlFlowOp(op, next_pc), is_conditional}};
|
||||
}
|
||||
|
||||
// gets a list of indeces and coarse categories in this pcodeop block
|
||||
// gets a list of indices and coarse categories in this pcodeop block
|
||||
static std::optional<std::map<size_t, CoarseFlow>>
|
||||
CoarseFlows(const std::vector<RemillPcodeOp> &ops, uint64_t next_pc) {
|
||||
std::map<size_t, CoarseFlow> res;
|
||||
@@ -118,7 +125,6 @@ static bool isUnconditionalNormal(CoarseFlow flow) {
|
||||
static std::optional<CoarseCategory>
|
||||
CoarseCategoryFromFlows(const std::map<size_t, CoarseFlow> &ops) {
|
||||
|
||||
|
||||
auto all_normal_effects = std::all_of(
|
||||
ops.begin(), ops.end(), [](const std::pair<size_t, CoarseFlow> &op) {
|
||||
return op.second.eff == CoarseEffect::kNormal;
|
||||
@@ -127,6 +133,17 @@ CoarseCategoryFromFlows(const std::map<size_t, CoarseFlow> &ops) {
|
||||
return CoarseCategory::kCatNormal;
|
||||
}
|
||||
|
||||
auto is_normal_or_intra = [](const std::pair<size_t, CoarseFlow> &op) {
|
||||
return op.second.eff == CoarseEffect::kNormal ||
|
||||
op.second.eff == CoarseEffect::kIntraInstruction;
|
||||
};
|
||||
auto all_normal_or_intra_effects =
|
||||
std::all_of(ops.begin(), ops.end(), is_normal_or_intra);
|
||||
|
||||
if (all_normal_or_intra_effects) {
|
||||
return CoarseCategory::kCatNormalWithIntraInstructionFlow;
|
||||
}
|
||||
|
||||
auto all_abnormal_effects = std::all_of(
|
||||
ops.begin(), ops.end(), [](const std::pair<size_t, CoarseFlow> &op) {
|
||||
return op.second.eff == CoarseEffect::kAbnormal;
|
||||
@@ -258,7 +275,7 @@ ExtractNonConditionalCategory(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Instruction::InstructionFlowCategory fst = cats[0];
|
||||
auto fst = cats[0];
|
||||
auto all_flows_equal = [&fst](Instruction::InstructionFlowCategory curr_cat) {
|
||||
return fst == curr_cat;
|
||||
};
|
||||
@@ -380,14 +397,14 @@ So in a coarse grained way we can just treat indirect/direct/interprocedural flo
|
||||
Either a fallthrough or an abnormal flow can be conditional
|
||||
|
||||
So the first step is to categorize a coarse grained control flow category which is one of:
|
||||
- Normal, in this case there are only fallthroughs, conditional or otherwise
|
||||
- Normal, in this case there are only fallthroughs, conditional or otherwise
|
||||
- Conditional Abnormal: either we have a CONDITIONAL_FALLTHROUGH followed by an kAbnormal
|
||||
- or we have a CONDITIONAL_ABNORMAL followed by a FALLTHROUGH
|
||||
- Abnormal: we have many ABNORMALs conditional or otherwise
|
||||
- Abnormal: we have many ABNORMALs conditional or otherwise
|
||||
|
||||
We forbid multiple conditionals in a flow because then we'd need to join conditions
|
||||
|
||||
After we find coarse categories and the flows follow these patterns, we determine if there is a constant context for each relevant flow.
|
||||
After we find coarse categories and the flows follow these patterns, we determine if there is a constant context for each relevant flow.
|
||||
|
||||
Finally we pass these coarse flows to a final categorizer to attempt to print these into a flow type
|
||||
*/
|
||||
@@ -410,10 +427,33 @@ ControlFlowStructureAnalysis::ComputeCategory(
|
||||
DLOG(ERROR) << "No coarse category found";
|
||||
return std::nullopt;
|
||||
}
|
||||
auto context_updater = this->BuildContextUpdater(std::move(entry_context));
|
||||
auto context_updater = BuildContextUpdater(entry_context);
|
||||
|
||||
if (*maybe_ccategory == CoarseCategory::kCatNormalWithIntraInstructionFlow) {
|
||||
// our control flow analysis for decoding contexts doesnt handle intraprocedural control flow,
|
||||
// so if we have a normal instruction with no decoding context updates then we are fine otherwise bail
|
||||
auto no_context_updates = std::all_of(
|
||||
ops.begin(), ops.end(), [&context_updater](const RemillPcodeOp &op) {
|
||||
return !op.outvar.has_value() ||
|
||||
!context_updater.GetRemillReg(*op.outvar).has_value();
|
||||
});
|
||||
|
||||
if (no_context_updates) {
|
||||
Instruction::NormalInsn norm_insn(
|
||||
(Instruction::FallthroughFlow(entry_context)));
|
||||
Instruction::InstructionFlowCategory ifc = norm_insn;
|
||||
return std::make_pair(ifc, std::nullopt);
|
||||
}
|
||||
DLOG(ERROR)
|
||||
<< "Had an instructon with intrainstruction flow, but also decoding context updates";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto flows = GetBoundContextsForFlows(ops, cc, context_updater);
|
||||
|
||||
switch (*maybe_ccategory) {
|
||||
case CoarseCategory::kCatNormalWithIntraInstructionFlow:
|
||||
return std::nullopt;
|
||||
case CoarseCategory::kCatAbnormal: return ExtractAbnormal(flows, ops);
|
||||
case CoarseCategory::kCatConditionalAbnormal:
|
||||
return ExtractConditionalAbnormal(flows, ops);
|
||||
@@ -421,6 +461,18 @@ ControlFlowStructureAnalysis::ComputeCategory(
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
ContextUpdater::GetRemillReg(const VarnodeData &outvar) {
|
||||
auto reg_name =
|
||||
engine.getRegisterName(outvar.space, outvar.offset, outvar.size);
|
||||
auto it = context_reg_mapping.find(reg_name);
|
||||
if (it != context_reg_mapping.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
// Applies a pcode op to the held context, this may produce a complete context
|
||||
void ContextUpdater::ApplyPcodeOp(const RemillPcodeOp &op) {
|
||||
if (!op.outvar) {
|
||||
@@ -428,36 +480,35 @@ void ContextUpdater::ApplyPcodeOp(const RemillPcodeOp &op) {
|
||||
}
|
||||
|
||||
auto out = *op.outvar;
|
||||
auto reg_name = this->engine.getRegisterName(out.space, out.offset, out.size);
|
||||
auto maybe_remill_reg_name = this->context_reg_mapping.find(reg_name);
|
||||
if (maybe_remill_reg_name == this->context_reg_mapping.end()) {
|
||||
auto maybe_remill_reg_name = GetRemillReg(out);
|
||||
if (!maybe_remill_reg_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto remill_reg_name = maybe_remill_reg_name->second;
|
||||
auto remill_reg_name = *maybe_remill_reg_name;
|
||||
|
||||
if (op.op == CPUI_COPY && isVarnodeInConstantSpace(op.vars[0])) {
|
||||
this->curr_context.UpdateContextReg(remill_reg_name, op.vars[0].offset);
|
||||
curr_context.UpdateContextReg(remill_reg_name, op.vars[0].offset);
|
||||
} else {
|
||||
this->curr_context.DropReg(remill_reg_name);
|
||||
curr_context.DropReg(remill_reg_name);
|
||||
}
|
||||
}
|
||||
|
||||
// May have a complete context
|
||||
std::optional<DecodingContext> ContextUpdater::GetContext() const {
|
||||
for (const auto &[_, remill_reg] : this->context_reg_mapping) {
|
||||
if (!this->curr_context.HasValueForReg(remill_reg)) {
|
||||
if (!curr_context.HasValueForReg(remill_reg)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
return this->curr_context;
|
||||
return curr_context;
|
||||
}
|
||||
|
||||
ContextUpdater ControlFlowStructureAnalysis::BuildContextUpdater(
|
||||
DecodingContext initial_context) {
|
||||
return ContextUpdater(this->context_reg_mapping, std::move(initial_context),
|
||||
this->engine);
|
||||
return ContextUpdater(context_reg_mapping, std::move(initial_context),
|
||||
engine);
|
||||
}
|
||||
|
||||
ContextUpdater::ContextUpdater(
|
||||
@@ -475,4 +526,4 @@ ControlFlowStructureAnalysis::ControlFlowStructureAnalysis(
|
||||
engine(engine_) {}
|
||||
|
||||
|
||||
} // namespace remill::sleigh
|
||||
} // namespace remill::sleigh
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sleigh/libsleigh.hh>
|
||||
#include <sleigh/pcoderaw.hh>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace remill::sleigh {
|
||||
@@ -33,6 +34,9 @@ class ContextUpdater {
|
||||
// Applies a pcode op to the held context, this may produce a complete context
|
||||
void ApplyPcodeOp(const RemillPcodeOp &op);
|
||||
|
||||
std::optional<std::string> GetRemillReg(const VarnodeData &);
|
||||
|
||||
|
||||
// May have a complete context
|
||||
std::optional<DecodingContext> GetContext() const;
|
||||
};
|
||||
@@ -47,9 +51,8 @@ class ControlFlowStructureAnalysis {
|
||||
ContextUpdater BuildContextUpdater(DecodingContext initial_context);
|
||||
|
||||
public:
|
||||
using SleighDecodingResult =
|
||||
std::optional<std::pair<Instruction::InstructionFlowCategory,
|
||||
std::optional<BranchTakenVar>>>;
|
||||
using SleighDecodingResult = std::optional<
|
||||
std::pair<Instruction::InstructionFlowCategory, MaybeBranchTakenVar>>;
|
||||
ControlFlowStructureAnalysis(
|
||||
const std::unordered_map<std::string, std::string> &context_reg_mapping,
|
||||
Sleigh &engine);
|
||||
@@ -60,4 +63,4 @@ class ControlFlowStructureAnalysis {
|
||||
uint64_t fallthrough_addr,
|
||||
DecodingContext entry_context);
|
||||
};
|
||||
} // namespace remill::sleigh
|
||||
} // namespace remill::sleigh
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <remill/Arch/Name.h>
|
||||
#include <remill/BC/ABI.h>
|
||||
#include <remill/BC/Util.h>
|
||||
#include <remill/BC/Version.h>
|
||||
#include <remill/OS/OS.h>
|
||||
|
||||
#include "Arch.h"
|
||||
|
||||
namespace remill::sleighppc {
|
||||
|
||||
class SleighPPCDecoder final : public remill::sleigh::SleighDecoder {
|
||||
public:
|
||||
SleighPPCDecoder(const remill::Arch &);
|
||||
|
||||
llvm::Value *LiftPcFromCurrPc(llvm::IRBuilder<> &, llvm::Value *,
|
||||
size_t) const override;
|
||||
|
||||
void InitializeSleighContext(remill::sleigh::SingleInstructionSleighContext &,
|
||||
const ContextValues &) const override;
|
||||
};
|
||||
|
||||
} // namespace remill::sleighppc
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "PPC.h"
|
||||
|
||||
#define INCLUDED_FROM_REMILL
|
||||
#include <remill/Arch/PPC/Runtime/State.h>
|
||||
|
||||
namespace remill {
|
||||
|
||||
namespace sleighppc {
|
||||
|
||||
static constexpr auto kPPCVLERegName = "VLEReg";
|
||||
|
||||
SleighPPCDecoder::SleighPPCDecoder(const remill::Arch &arch)
|
||||
: SleighDecoder(arch, "ppc_64_isa_vle_be.sla", "ppc_64.pspec",
|
||||
{{"vle", kPPCVLERegName}}, {}) {}
|
||||
|
||||
llvm::Value *SleighPPCDecoder::LiftPcFromCurrPc(llvm::IRBuilder<> &bldr,
|
||||
llvm::Value *curr_pc,
|
||||
size_t curr_insn_size) const {
|
||||
// PC on thumb points to the next instructions next.
|
||||
return bldr.CreateAdd(
|
||||
curr_pc, llvm::ConstantInt::get(curr_pc->getType(), curr_insn_size));
|
||||
}
|
||||
|
||||
void SleighPPCDecoder::InitializeSleighContext(
|
||||
remill::sleigh::SingleInstructionSleighContext &ctxt,
|
||||
const ContextValues &context_values) const {
|
||||
// If the context value mappings specify a value for the VLE register, let's pass that into
|
||||
// Sleigh.
|
||||
//
|
||||
// Otherwise, default to VLE off.
|
||||
sleigh::SetContextRegisterValueInSleigh(kPPCVLERegName, "vle", 0, ctxt,
|
||||
context_values);
|
||||
}
|
||||
|
||||
class SleighPPCArch : public ArchBase {
|
||||
public:
|
||||
SleighPPCArch(llvm::LLVMContext *context_, OSName os_name_,
|
||||
ArchName arch_name_)
|
||||
: ArchBase(context_, os_name_, arch_name_),
|
||||
decoder(*this) {}
|
||||
virtual ~SleighPPCArch() = default;
|
||||
|
||||
DecodingContext CreateInitialContext(void) const override {
|
||||
return DecodingContext().PutContextReg(kPPCVLERegName, 0);
|
||||
}
|
||||
|
||||
std::string_view StackPointerRegisterName(void) const override {
|
||||
return "R1";
|
||||
}
|
||||
|
||||
std::string_view ProgramCounterRegisterName(void) const override {
|
||||
return "PC";
|
||||
}
|
||||
|
||||
OperandLifter::OpLifterPtr
|
||||
DefaultLifter(const remill::IntrinsicTable &intrinsics) const override {
|
||||
return decoder.GetOpLifter();
|
||||
}
|
||||
|
||||
bool DecodeInstruction(uint64_t address, std::string_view instr_bytes,
|
||||
Instruction &inst,
|
||||
DecodingContext context) const override {
|
||||
return decoder.DecodeInstruction(address, instr_bytes, inst, context);
|
||||
}
|
||||
|
||||
uint64_t MinInstructionAlign(const DecodingContext &) const override {
|
||||
return 2;
|
||||
}
|
||||
|
||||
uint64_t MinInstructionSize(const DecodingContext &) const override {
|
||||
return 2;
|
||||
}
|
||||
|
||||
uint64_t MaxInstructionSize(const DecodingContext &, bool) const override {
|
||||
return 4;
|
||||
}
|
||||
|
||||
llvm::CallingConv::ID DefaultCallingConv(void) const override {
|
||||
return llvm::CallingConv::C;
|
||||
}
|
||||
|
||||
llvm::Triple Triple(void) const override {
|
||||
auto triple = BasicTriple();
|
||||
triple.setArch(llvm::Triple::ppc);
|
||||
return triple;
|
||||
}
|
||||
|
||||
llvm::DataLayout DataLayout(void) const override {
|
||||
return llvm::DataLayout("e-m:e-p:32:32-i32:32-i64:64-f64:64-n32:64-S128");
|
||||
}
|
||||
|
||||
void PopulateRegisterTable(void) const override {
|
||||
CHECK_NOTNULL(context);
|
||||
|
||||
reg_by_offset.resize(sizeof(PPCState));
|
||||
|
||||
auto u8 = llvm::Type::getInt8Ty(*context);
|
||||
auto u32 = llvm::Type::getInt32Ty(*context);
|
||||
auto u64 = llvm::Type::getInt64Ty(*context);
|
||||
|
||||
auto f64 = llvm::Type::getDoubleTy(*context);
|
||||
|
||||
#define OFFSET_OF(type, access) \
|
||||
(reinterpret_cast<uintptr_t>(&reinterpret_cast<const volatile char &>( \
|
||||
static_cast<type *>(nullptr)->access)))
|
||||
|
||||
#define REG(name, access, type) \
|
||||
AddRegister(#name, type, OFFSET_OF(PPCState, access), nullptr)
|
||||
|
||||
#define SUB_REG(name, access, type, parent_reg_name) \
|
||||
AddRegister(#name, type, OFFSET_OF(PPCState, access), #parent_reg_name)
|
||||
|
||||
REG(R0, gpr.r0.qword, u64);
|
||||
REG(R1, gpr.r1.qword, u64);
|
||||
REG(R2, gpr.r2.qword, u64);
|
||||
REG(R3, gpr.r3.qword, u64);
|
||||
REG(R4, gpr.r4.qword, u64);
|
||||
REG(R5, gpr.r5.qword, u64);
|
||||
REG(R6, gpr.r6.qword, u64);
|
||||
REG(R7, gpr.r7.qword, u64);
|
||||
REG(R8, gpr.r8.qword, u64);
|
||||
REG(R9, gpr.r9.qword, u64);
|
||||
REG(R10, gpr.r10.qword, u64);
|
||||
REG(R11, gpr.r11.qword, u64);
|
||||
REG(R12, gpr.r12.qword, u64);
|
||||
REG(R13, gpr.r13.qword, u64);
|
||||
REG(R14, gpr.r14.qword, u64);
|
||||
REG(R15, gpr.r15.qword, u64);
|
||||
REG(R16, gpr.r16.qword, u64);
|
||||
REG(R17, gpr.r17.qword, u64);
|
||||
REG(R18, gpr.r18.qword, u64);
|
||||
REG(R19, gpr.r19.qword, u64);
|
||||
REG(R20, gpr.r20.qword, u64);
|
||||
REG(R21, gpr.r21.qword, u64);
|
||||
REG(R22, gpr.r22.qword, u64);
|
||||
REG(R23, gpr.r23.qword, u64);
|
||||
REG(R24, gpr.r24.qword, u64);
|
||||
REG(R25, gpr.r25.qword, u64);
|
||||
REG(R26, gpr.r26.qword, u64);
|
||||
REG(R27, gpr.r27.qword, u64);
|
||||
REG(R28, gpr.r28.qword, u64);
|
||||
REG(R29, gpr.r29.qword, u64);
|
||||
REG(R30, gpr.r30.qword, u64);
|
||||
REG(R31, gpr.r31.qword, u64);
|
||||
|
||||
REG(F0, fpr.f0.qword, f64);
|
||||
REG(F1, fpr.f1.qword, f64);
|
||||
REG(F2, fpr.f2.qword, f64);
|
||||
REG(F3, fpr.f3.qword, f64);
|
||||
REG(F4, fpr.f4.qword, f64);
|
||||
REG(F5, fpr.f5.qword, f64);
|
||||
REG(F6, fpr.f6.qword, f64);
|
||||
REG(F7, fpr.f7.qword, f64);
|
||||
REG(F8, fpr.f8.qword, f64);
|
||||
REG(F9, fpr.f9.qword, f64);
|
||||
REG(F10, fpr.f10.qword, f64);
|
||||
REG(F11, fpr.f11.qword, f64);
|
||||
REG(F12, fpr.f12.qword, f64);
|
||||
REG(F13, fpr.f13.qword, f64);
|
||||
REG(F14, fpr.f14.qword, f64);
|
||||
REG(F15, fpr.f15.qword, f64);
|
||||
REG(F16, fpr.f16.qword, f64);
|
||||
REG(F17, fpr.f17.qword, f64);
|
||||
REG(F18, fpr.f18.qword, f64);
|
||||
REG(F19, fpr.f19.qword, f64);
|
||||
REG(F20, fpr.f20.qword, f64);
|
||||
REG(F21, fpr.f21.qword, f64);
|
||||
REG(F22, fpr.f22.qword, f64);
|
||||
REG(F23, fpr.f23.qword, f64);
|
||||
REG(F24, fpr.f24.qword, f64);
|
||||
REG(F25, fpr.f25.qword, f64);
|
||||
REG(F26, fpr.f26.qword, f64);
|
||||
REG(F27, fpr.f27.qword, f64);
|
||||
REG(F28, fpr.f28.qword, f64);
|
||||
REG(F29, fpr.f29.qword, f64);
|
||||
REG(F30, fpr.f30.qword, f64);
|
||||
|
||||
REG(CRALL, iar.cr.qword, u64);
|
||||
REG(CTR, iar.ctr.qword, u64);
|
||||
REG(LR, iar.lr.qword, u64);
|
||||
REG(XER, iar.xer.dword, u32);
|
||||
REG(SPEFCR, iar.spefscr.qword, u64);
|
||||
REG(ACC, iar.acc.qword, u64);
|
||||
|
||||
// These are actually bitflags within XER and CR respectively. These would
|
||||
// normally be subregisters however, Sleigh treats these as entirely
|
||||
// separate registers of size 1.
|
||||
REG(XER_SO, xer_flags.so, u8);
|
||||
REG(XER_OV, xer_flags.ov, u8);
|
||||
REG(XER_CA, xer_flags.ca, u8);
|
||||
REG(XER_COUNT, xer_flags.sl, u8);
|
||||
|
||||
REG(CR0, cr_flags.cr0, u8);
|
||||
REG(CR1, cr_flags.cr1, u8);
|
||||
REG(CR2, cr_flags.cr2, u8);
|
||||
REG(CR3, cr_flags.cr3, u8);
|
||||
REG(CR4, cr_flags.cr4, u8);
|
||||
REG(CR5, cr_flags.cr5, u8);
|
||||
REG(CR6, cr_flags.cr6, u8);
|
||||
REG(CR7, cr_flags.cr7, u8);
|
||||
|
||||
REG(TBLR, tbr.tbl.qword, u64);
|
||||
REG(TBUR, tbr.tbu.qword, u64);
|
||||
|
||||
REG(SPR103, sprg.r3.qword, u64);
|
||||
REG(SPR104, sprg.r4.qword, u64);
|
||||
REG(SPR105, sprg.r5.qword, u64);
|
||||
REG(SPR106, sprg.r6.qword, u64);
|
||||
REG(SPR107, sprg.r7.qword, u64);
|
||||
|
||||
REG(SPR203, l1cfg.r0.qword, u64);
|
||||
REG(SPR204, l1cfg.r1.qword, u64);
|
||||
|
||||
REG(PC, pc, u64);
|
||||
|
||||
REG(TEA, signals.tea.qword, u64);
|
||||
}
|
||||
|
||||
void
|
||||
FinishLiftedFunctionInitialization(llvm::Module *module,
|
||||
llvm::Function *bb_func) const override {
|
||||
auto &context = module->getContext();
|
||||
const auto addr = llvm::Type::getInt64Ty(context);
|
||||
|
||||
auto &entry_block = bb_func->getEntryBlock();
|
||||
llvm::IRBuilder<> ir(&entry_block);
|
||||
|
||||
const auto pc_arg = NthArgument(bb_func, kPCArgNum);
|
||||
const auto state_ptr_arg = NthArgument(bb_func, kStatePointerArgNum);
|
||||
|
||||
auto mk_alloca = [&](auto &from) {
|
||||
return ir.CreateAlloca(addr, nullptr, from.data());
|
||||
};
|
||||
ir.CreateStore(pc_arg, mk_alloca(kNextPCVariableName));
|
||||
ir.CreateStore(pc_arg, mk_alloca(kIgnoreNextPCVariableName));
|
||||
|
||||
std::ignore = RegisterByName(kPCVariableName)->AddressOf(state_ptr_arg, ir);
|
||||
}
|
||||
|
||||
private:
|
||||
SleighPPCDecoder decoder;
|
||||
};
|
||||
|
||||
} // namespace sleighppc
|
||||
|
||||
Arch::ArchPtr Arch::GetSleighPPC(llvm::LLVMContext *context_,
|
||||
remill::OSName os_name_,
|
||||
remill::ArchName arch_name_) {
|
||||
return std::make_unique<sleighppc::SleighPPCArch>(context_, os_name_,
|
||||
arch_name_);
|
||||
}
|
||||
|
||||
} // namespace remill
|
||||
@@ -22,8 +22,9 @@ class SleighThumb2Decoder final : public remill::sleigh::SleighDecoder {
|
||||
virtual llvm::Value *LiftPcFromCurrPc(llvm::IRBuilder<> &bldr, llvm::Value *,
|
||||
size_t curr_insn_size) const final;
|
||||
|
||||
void InitializeSleighContext(
|
||||
remill::sleigh::SingleInstructionSleighContext &ctxt) const final;
|
||||
void
|
||||
InitializeSleighContext(remill::sleigh::SingleInstructionSleighContext &ctxt,
|
||||
const ContextValues &context_values) const final;
|
||||
};
|
||||
} // namespace sleighthumb2
|
||||
} // namespace remill
|
||||
} // namespace remill
|
||||
|
||||
@@ -41,7 +41,8 @@ SleighThumb2Decoder::SleighThumb2Decoder(const remill::Arch &arch)
|
||||
|
||||
|
||||
void SleighThumb2Decoder::InitializeSleighContext(
|
||||
remill::sleigh::SingleInstructionSleighContext &ctxt) const {
|
||||
remill::sleigh::SingleInstructionSleighContext &ctxt,
|
||||
const ContextValues &) const {
|
||||
ctxt.GetContext().setVariableDefault("TMode", 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,8 +42,9 @@ class SleighX86Decoder final : public SleighDecoder {
|
||||
{}, {}) {}
|
||||
|
||||
// The x86 default context is sufficient. No context register assignments are required.
|
||||
void InitializeSleighContext(
|
||||
remill::sleigh::SingleInstructionSleighContext &ctxt) const override {}
|
||||
void
|
||||
InitializeSleighContext(remill::sleigh::SingleInstructionSleighContext &ctxt,
|
||||
const ContextValues &) const override {}
|
||||
|
||||
llvm::Value *LiftPcFromCurrPc(llvm::IRBuilder<> &bldr, llvm::Value *curr_pc,
|
||||
size_t curr_insn_size) const final {
|
||||
|
||||
@@ -31,6 +31,7 @@ add_library(remill_bc STATIC
|
||||
Optimizer.cpp
|
||||
TraceLifter.cpp
|
||||
SleighLifter.cpp
|
||||
PcodeCFG.cpp
|
||||
Util.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
#include <remill/BC/PCodeCFG.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <sleigh/op.hh>
|
||||
#include <sleigh/opcodes.hh>
|
||||
#include <sleigh/pcoderaw.hh>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/Arch/Sleigh/ControlFlowStructuring.h"
|
||||
|
||||
namespace remill {
|
||||
namespace sleigh {
|
||||
|
||||
PcodeCFG CreateCFG(const std::vector<RemillPcodeOp> &linear_ops) {
|
||||
return PcodeCFGBuilder(linear_ops).Build();
|
||||
}
|
||||
|
||||
|
||||
std::vector<size_t> PcodeCFGBuilder::GetBlockStarts() const {
|
||||
std::vector<size_t> res = {0};
|
||||
|
||||
for (size_t curr_index = 0; curr_index < linear_ops.size(); curr_index += 1) {
|
||||
auto interproc = GetIntraProcTargets(curr_index);
|
||||
res.insert(res.end(), interproc.begin(), interproc.end());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
PcodeBlock PcodeCFGBuilder::BuildBlock(size_t start_ind,
|
||||
size_t next_start) const {
|
||||
std::vector<RemillPcodeOp> ops;
|
||||
|
||||
std::copy(linear_ops.begin() + start_ind, linear_ops.begin() + next_start,
|
||||
std::back_inserter(ops));
|
||||
|
||||
return PcodeBlock(start_ind, std::move(ops),
|
||||
GetBlockExitsForIndex(next_start - 1));
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
struct IntraProcTransferCollector {
|
||||
|
||||
static std::vector<size_t> CollectIntraProcTransfers(const BlockExit &exit) {
|
||||
IntraProcTransferCollector collector;
|
||||
std::visit(collector, exit);
|
||||
return collector.targets;
|
||||
}
|
||||
|
||||
std::vector<size_t> targets;
|
||||
|
||||
void operator()(const IntrainstructionIndex &ex) {
|
||||
targets.push_back(ex.target_block_index);
|
||||
}
|
||||
|
||||
void operator()(const Exit &ex) {
|
||||
std::visit(*this, ex);
|
||||
}
|
||||
|
||||
void operator()(const ConditionalExit &ex) {
|
||||
std::visit(*this, ex.true_branch);
|
||||
std::visit(*this, ex.false_branch);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::vector<size_t> PcodeCFGBuilder::GetIntraProcTargets(size_t index) const {
|
||||
auto ex = GetBlockExitsForIndex(index);
|
||||
return IntraProcTransferCollector::CollectIntraProcTransfers(ex);
|
||||
}
|
||||
|
||||
BlockExit PcodeCFGBuilder::GetBlockExitsForIndex(size_t index) const {
|
||||
CHECK(index < linear_ops.size());
|
||||
const auto &curr_op = linear_ops[index];
|
||||
|
||||
auto build_direct_target_exit = [](VarnodeData target,
|
||||
size_t curr_ind) -> Exit {
|
||||
if (isVarnodeInConstantSpace(target)) {
|
||||
// need to treat as signed?
|
||||
return IntrainstructionIndex{curr_ind + target.offset};
|
||||
}
|
||||
return InstrExit{};
|
||||
};
|
||||
switch (curr_op.op) {
|
||||
case CPUI_BRANCH:
|
||||
case CPUI_CALL: {
|
||||
return Exit{build_direct_target_exit(curr_op.vars[0], index)};
|
||||
}
|
||||
case CPUI_CBRANCH: {
|
||||
|
||||
auto fallthrough_exit = [this, index]() -> Exit {
|
||||
// if we are not the last pcodeop then we have an intraproc fallthrough
|
||||
if (index < linear_ops.size() - 1) {
|
||||
return IntrainstructionIndex{index + 1};
|
||||
}
|
||||
return InstrExit{};
|
||||
}();
|
||||
|
||||
auto taken_exit = build_direct_target_exit(curr_op.vars[0], index);
|
||||
|
||||
return ConditionalExit{taken_exit, fallthrough_exit};
|
||||
}
|
||||
case CPUI_CALLIND:
|
||||
case CPUI_BRANCHIND: {
|
||||
return Exit{InstrExit{}};
|
||||
}
|
||||
|
||||
default: {
|
||||
return Exit{InstrExit{}};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PcodeBlock::PcodeBlock(size_t base_index)
|
||||
: base_index(base_index),
|
||||
ops(),
|
||||
block_exit(Exit{InstrExit{}}) {}
|
||||
|
||||
PcodeCFG PcodeCFGBuilder::Build() const {
|
||||
|
||||
auto starts = GetBlockStarts();
|
||||
|
||||
// De-duplicate and sort the block starts. We want to iterate in order.
|
||||
std::set s(starts.begin(), starts.end());
|
||||
starts.assign(s.begin(), s.end());
|
||||
|
||||
std::map<size_t, PcodeBlock> blocks;
|
||||
|
||||
if (linear_ops.empty()) {
|
||||
// There is no insturction at 0 to build a block at
|
||||
// build an empty block so we transfer through to exit by terminating the block
|
||||
blocks.emplace(0, PcodeBlock(0));
|
||||
return blocks;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < starts.size(); i++) {
|
||||
auto next_start =
|
||||
(i + 1) < starts.size() ? starts[i + 1] : linear_ops.size();
|
||||
blocks.emplace(starts[i], BuildBlock(starts[i], next_start));
|
||||
}
|
||||
|
||||
return PcodeCFG(blocks);
|
||||
}
|
||||
|
||||
|
||||
PcodeCFGBuilder::PcodeCFGBuilder(const std::vector<RemillPcodeOp> &linear_ops)
|
||||
: linear_ops(linear_ops) {}
|
||||
|
||||
PcodeCFG::PcodeCFG(std::map<size_t, PcodeBlock> blocks)
|
||||
: blocks(std::move(blocks)) {}
|
||||
|
||||
PcodeBlock::PcodeBlock(size_t base_index, std::vector<RemillPcodeOp> ops,
|
||||
BlockExit block_exit)
|
||||
: base_index(base_index),
|
||||
ops(std::move(ops)),
|
||||
block_exit(std::move(block_exit)) {}
|
||||
|
||||
} // namespace sleigh
|
||||
} // namespace remill
|
||||
+246
-106
@@ -18,10 +18,13 @@
|
||||
#include <lib/Arch/Sleigh/Arch.h>
|
||||
#include <lib/Arch/Sleigh/ControlFlowStructuring.h>
|
||||
#include <llvm/ADT/StringExtras.h>
|
||||
#include <llvm/IR/BasicBlock.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/Value.h>
|
||||
#include <llvm/IR/Verifier.h>
|
||||
#include <remill/BC/ABI.h>
|
||||
#include <remill/BC/IntrinsicTable.h>
|
||||
#include <remill/BC/PCodeCFG.h>
|
||||
#include <remill/BC/SleighLifter.h>
|
||||
#include <remill/BC/Util.h>
|
||||
|
||||
@@ -30,9 +33,11 @@
|
||||
#include <sleigh/pcoderaw.hh>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <variant>
|
||||
|
||||
#include "remill/BC/InstructionLifter.h"
|
||||
|
||||
|
||||
namespace remill {
|
||||
|
||||
|
||||
@@ -57,6 +62,7 @@ std::string DumpPcode(Sleigh &engine, const remill::sleigh::RemillPcodeOp &op) {
|
||||
ss << " = ";
|
||||
}
|
||||
for (size_t i = 0; i < op.vars.size(); ++i) {
|
||||
ss << ' ';
|
||||
auto iv = op.vars[i];
|
||||
print_vardata(engine, ss, iv);
|
||||
}
|
||||
@@ -85,9 +91,28 @@ static llvm::Value *ExtractOverflowBitFromCallToIntrinsic(
|
||||
// The value at index 1 is the overflow bit.
|
||||
return bldr.CreateExtractValue(res_val, {1});
|
||||
}
|
||||
|
||||
using BitShiftFunction = std::function<llvm::Value *(
|
||||
llvm::Value *, llvm::Value *, llvm::IRBuilder<> &)>;
|
||||
|
||||
llvm::Value *CreatePcodeBitShift(llvm::Value *lhs, llvm::Value *rhs,
|
||||
llvm::IRBuilder<> &bldr,
|
||||
const BitShiftFunction &bitshift_func) {
|
||||
if (lhs->getType() != rhs->getType()) {
|
||||
rhs = bldr.CreateZExtOrTrunc(rhs, lhs->getType());
|
||||
}
|
||||
// If the number of bits we're shifting exceeds the bit width of the
|
||||
// other operand, the result should be zero.
|
||||
auto max_shift = llvm::ConstantInt::get(lhs->getType(),
|
||||
lhs->getType()->getIntegerBitWidth());
|
||||
return bldr.CreateSelect(bldr.CreateICmpSGE(rhs, max_shift),
|
||||
llvm::ConstantInt::get(lhs->getType(), 0),
|
||||
bitshift_func(lhs, rhs, bldr));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
class SleighLifter::PcodeToLLVMEmitIntoBlock {
|
||||
private:
|
||||
class Parameter {
|
||||
public:
|
||||
@@ -212,6 +237,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
|
||||
|
||||
llvm::BasicBlock *target_block;
|
||||
const sleigh::PcodeBlock *pcode_block{nullptr};
|
||||
llvm::Value *state_pointer;
|
||||
llvm::LLVMContext &context;
|
||||
const Instruction &insn;
|
||||
@@ -254,7 +280,8 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
SleighLifter::PcodeToLLVMEmitIntoBlock &lifter,
|
||||
VarnodeData lhs_constant,
|
||||
VarnodeData rhs_unfolded_value) {
|
||||
assert(isVarnodeInConstantSpace(lhs_constant));
|
||||
CHECK(isVarnodeInConstantSpace(lhs_constant));
|
||||
DLOG(INFO) << "Adding (" << lhs_constant.offset << ") to map";
|
||||
this->current_replacements.insert(
|
||||
{lhs_constant.offset, lifter.LiftParamPtr(bldr, rhs_unfolded_value)});
|
||||
}
|
||||
@@ -265,9 +292,14 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
this->used_values.clear();
|
||||
}
|
||||
|
||||
// NOTE(wtan): this may end up replacing constants that shouldn't be replaced
|
||||
// if the claim_eq happens to be used on constant values that are used elsewhere
|
||||
// In practice, we don't expect this to happen since the program will be mapped
|
||||
// to a higher address space so collisions should be rare
|
||||
llvm::Value *LiftOffsetOrReplace(llvm::IRBuilder<> &bldr,
|
||||
VarnodeData target,
|
||||
llvm::Type *target_type) {
|
||||
DLOG(INFO) << "Fetching (" << target.offset << ") from map";
|
||||
if (this->current_replacements.find(target.offset) !=
|
||||
this->current_replacements.end()) {
|
||||
|
||||
@@ -288,6 +320,13 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
|
||||
return llvm::ConstantInt::get(target_type, target.offset);
|
||||
}
|
||||
|
||||
// Returns true if the equality claim was used or if no equality claims were declared
|
||||
// if the equality claim is not used at all when lifting an instruction,
|
||||
// this can indicate that there is a bug
|
||||
bool IsEqualityUsed() const {
|
||||
return !used_values.empty() || current_replacements.empty();
|
||||
}
|
||||
};
|
||||
|
||||
UniqueRegSpace uniques;
|
||||
@@ -297,11 +336,15 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
// Generic sleigh arch
|
||||
std::vector<std::string> user_op_names;
|
||||
|
||||
llvm::BasicBlock *entry_block;
|
||||
llvm::BasicBlock *exit_block;
|
||||
|
||||
size_t curr_id;
|
||||
|
||||
const std::optional<remill::sleigh::BranchTakenVar> &to_lift_btaken;
|
||||
const sleigh::MaybeBranchTakenVar &to_lift_btaken;
|
||||
|
||||
std::unordered_map<size_t, llvm::BasicBlock *> start_index_to_block;
|
||||
|
||||
|
||||
void UpdateStatus(LiftStatus new_status, OpCode opc) {
|
||||
if (new_status != LiftStatus::kLiftedInstruction) {
|
||||
@@ -312,12 +355,33 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
}
|
||||
|
||||
llvm::BasicBlock *GetBlock(size_t target) const {
|
||||
auto blk = start_index_to_block.find(target);
|
||||
if (blk != start_index_to_block.end()) {
|
||||
return blk->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
PcodeToLLVMEmitIntoBlock(
|
||||
llvm::BasicBlock *target_block, llvm::Value *state_pointer,
|
||||
const Instruction &insn, SleighLifter &insn_lifter_parent,
|
||||
std::vector<std::string> user_op_names_, llvm::BasicBlock *exit_block_,
|
||||
const std::optional<remill::sleigh::BranchTakenVar> &to_lift_btaken_)
|
||||
llvm::BasicBlock *GetOrCreateBlock(size_t target) {
|
||||
if (auto blk = GetBlock(target)) {
|
||||
return blk;
|
||||
}
|
||||
|
||||
auto newblk = llvm::BasicBlock::Create(this->exit_block->getContext(), "",
|
||||
this->exit_block->getParent());
|
||||
|
||||
this->start_index_to_block[target] = newblk;
|
||||
return newblk;
|
||||
}
|
||||
|
||||
PcodeToLLVMEmitIntoBlock(llvm::BasicBlock *target_block,
|
||||
llvm::Value *state_pointer, const Instruction &insn,
|
||||
SleighLifter &insn_lifter_parent,
|
||||
std::vector<std::string> user_op_names_,
|
||||
llvm::BasicBlock *exit_block_,
|
||||
const sleigh::MaybeBranchTakenVar &to_lift_btaken_)
|
||||
: target_block(target_block),
|
||||
state_pointer(state_pointer),
|
||||
context(target_block->getContext()),
|
||||
@@ -327,6 +391,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
uniques(target_block->getContext()),
|
||||
unknown_regs(target_block->getContext()),
|
||||
user_op_names(user_op_names_),
|
||||
entry_block(target_block),
|
||||
exit_block(exit_block_),
|
||||
curr_id(0),
|
||||
to_lift_btaken(to_lift_btaken_) {}
|
||||
@@ -373,12 +438,15 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
return *res;
|
||||
}
|
||||
|
||||
// Uniques must be allocated in the entry block
|
||||
llvm::IRBuilder<> entry_bldr(entry_block);
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
auto reg_ptr = this->unknown_regs.GetUniquePtr(target_vnode.offset,
|
||||
target_vnode.size, bldr);
|
||||
auto reg_ptr = this->unknown_regs.GetUniquePtr(
|
||||
target_vnode.offset, target_vnode.size, entry_bldr);
|
||||
print_vardata(this->insn_lifter_parent.GetEngine(), ss, target_vnode);
|
||||
DLOG(ERROR) << "Creating unique for unkown register: " << ss.str() << " "
|
||||
DLOG(ERROR) << "Creating unique for unknown register: " << ss.str() << " "
|
||||
<< reg_ptr->getName().str();
|
||||
|
||||
return RegisterValue::CreateRegister(reg_ptr);
|
||||
@@ -412,7 +480,11 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
|
||||
return ConstantValue::CreatConstant(cst_v);
|
||||
} else if (space_name == "unique") {
|
||||
auto reg_ptr = this->uniques.GetUniquePtr(vnode.offset, vnode.size, bldr);
|
||||
// Uniques must be allocated in the entry block
|
||||
llvm::IRBuilder<> entry_bldr(entry_block);
|
||||
|
||||
auto reg_ptr =
|
||||
this->uniques.GetUniquePtr(vnode.offset, vnode.size, entry_bldr);
|
||||
return RegisterValue::CreateRegister(reg_ptr);
|
||||
} else {
|
||||
LOG(FATAL) << "Unhandled memory space: " << space_name;
|
||||
@@ -446,7 +518,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
|
||||
LiftStatus
|
||||
LiftRequireOutParam(std::function<LiftStatus(VarnodeData)> inner_lift,
|
||||
VarnodeData *outvar) {
|
||||
std::optional<VarnodeData> outvar) {
|
||||
if (outvar) {
|
||||
return inner_lift(*outvar);
|
||||
} else {
|
||||
@@ -457,7 +529,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
|
||||
LiftStatus LiftStoreIntoOutParam(llvm::IRBuilder<> &bldr,
|
||||
llvm::Value *inner_lifted,
|
||||
VarnodeData *outvar) {
|
||||
std::optional<VarnodeData> outvar) {
|
||||
return this->LiftRequireOutParam(
|
||||
[&bldr, this, inner_lifted](VarnodeData out_param_data) {
|
||||
auto ptr = this->LiftParamPtr(bldr, out_param_data);
|
||||
@@ -468,17 +540,18 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
|
||||
LiftStatus LiftUnaryOpWithFloatIntrinsic(
|
||||
llvm::IRBuilder<> &bldr,
|
||||
llvm::Intrinsic::IndependentIntrinsics intrinsic_id, VarnodeData *outvar,
|
||||
VarnodeData input_var) {
|
||||
llvm::Intrinsic::IndependentIntrinsics intrinsic_id,
|
||||
std::optional<VarnodeData> outvar, VarnodeData input_var) {
|
||||
auto inval = this->LiftFloatInParam(bldr, input_var);
|
||||
|
||||
if (!inval) {
|
||||
return LiftStatus::kLiftedUnsupportedInstruction;
|
||||
}
|
||||
|
||||
llvm::Function *intrinsic = llvm::Intrinsic::getDeclaration(
|
||||
bldr.GetInsertBlock()->getModule(), intrinsic_id);
|
||||
llvm::Value *intrinsic_args[] = {*inval};
|
||||
llvm::Function *intrinsic =
|
||||
llvm::Intrinsic::getDeclaration(bldr.GetInsertBlock()->getModule(),
|
||||
intrinsic_id, {(*inval)->getType()});
|
||||
return this->LiftStoreIntoOutParam(
|
||||
bldr,
|
||||
this->CastFloatResult(bldr, *outvar,
|
||||
@@ -496,7 +569,8 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
|
||||
LiftStatus LiftFloatUnop(llvm::IRBuilder<> &bldr, OpCode opc,
|
||||
VarnodeData *outvar, VarnodeData input_var) {
|
||||
std::optional<VarnodeData> outvar,
|
||||
VarnodeData input_var) {
|
||||
switch (opc) {
|
||||
case OpCode::CPUI_FLOAT_NEG: {
|
||||
auto negate_inval = this->LiftFloatInParam(bldr, input_var);
|
||||
@@ -530,7 +604,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
auto nan_inval = this->LiftFloatInParam(bldr, input_var);
|
||||
if (nan_inval.has_value()) {
|
||||
// LLVM trunk has an `isnan` intrinsic but to support older versions, I think we need to do this.
|
||||
auto *isnan_check = bldr.CreateZExt(
|
||||
auto isnan_check = bldr.CreateZExt(
|
||||
bldr.CreateNot(bldr.CreateFCmpORD(*nan_inval, *nan_inval)),
|
||||
llvm::IntegerType::get(this->context, outvar->size * 8));
|
||||
return this->LiftStoreIntoOutParam(bldr, isnan_check, outvar);
|
||||
@@ -539,9 +613,9 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
case OpCode::CPUI_FLOAT_INT2FLOAT: {
|
||||
auto int2float_inval = this->LiftIntegerInParam(bldr, input_var);
|
||||
if (int2float_inval.has_value()) {
|
||||
auto *converted = bldr.CreateSIToFP(
|
||||
*int2float_inval, llvm::Type::getFloatTy(this->context));
|
||||
auto new_float_type = this->GetFloatTypeOfByteSize(outvar->size);
|
||||
if (int2float_inval.has_value() && new_float_type) {
|
||||
auto converted = bldr.CreateSIToFP(*int2float_inval, *new_float_type);
|
||||
return this->LiftStoreIntoOutParam(
|
||||
bldr, this->CastFloatResult(bldr, *outvar, converted), outvar);
|
||||
}
|
||||
@@ -565,7 +639,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
auto trunc_inval = this->LiftFloatInParam(bldr, input_var);
|
||||
if (trunc_inval.has_value()) {
|
||||
// Should this be UI?
|
||||
auto *converted = bldr.CreateFPToSI(
|
||||
auto converted = bldr.CreateFPToSI(
|
||||
*trunc_inval,
|
||||
llvm::IntegerType::get(this->context, outvar->size * 8));
|
||||
return this->LiftStoreIntoOutParam(bldr, converted, outvar);
|
||||
@@ -580,7 +654,8 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
|
||||
LiftStatus LiftUnaryOp(llvm::IRBuilder<> &bldr, OpCode opc,
|
||||
VarnodeData *outvar, VarnodeData input_var) {
|
||||
std::optional<VarnodeData> outvar,
|
||||
VarnodeData input_var) {
|
||||
|
||||
auto res = this->LiftFloatUnop(bldr, opc, outvar, input_var);
|
||||
if (res != LiftStatus::kLiftedUnsupportedInstruction) {
|
||||
@@ -620,8 +695,8 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
// directs dont read the address of the variable, the offset is the jump
|
||||
// TODO(Ian): handle other address spaces
|
||||
if (isVarnodeInConstantSpace(input_var)) {
|
||||
DLOG(ERROR) << "Internal control flow not supported";
|
||||
return LiftStatus::kLiftedUnsupportedInstruction;
|
||||
this->TerminateBlock();
|
||||
return LiftStatus::kLiftedInstruction;
|
||||
}
|
||||
|
||||
auto input_val = this->replacement_cont.LiftOffsetOrReplace(
|
||||
@@ -647,11 +722,11 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
case OpCode::CPUI_INT_SEXT: {
|
||||
auto zext_inval = this->LiftIntegerInParam(bldr, input_var);
|
||||
if (zext_inval.has_value()) {
|
||||
auto *zext_type =
|
||||
auto zext_type =
|
||||
llvm::IntegerType::get(this->context, outvar->size * 8);
|
||||
auto *zext_op = (opc == OpCode::CPUI_INT_ZEXT)
|
||||
? bldr.CreateZExt(*zext_inval, zext_type)
|
||||
: bldr.CreateSExt(*zext_inval, zext_type);
|
||||
auto zext_op = (opc == OpCode::CPUI_INT_ZEXT)
|
||||
? bldr.CreateZExt(*zext_inval, zext_type)
|
||||
: bldr.CreateSExt(*zext_inval, zext_type);
|
||||
return this->LiftStoreIntoOutParam(bldr, zext_op, outvar);
|
||||
}
|
||||
break;
|
||||
@@ -700,24 +775,66 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
static std::unordered_set<OpCode> INTEGER_COMP_OPS;
|
||||
|
||||
|
||||
struct VisitExit {
|
||||
PcodeToLLVMEmitIntoBlock &lifter;
|
||||
|
||||
llvm::BasicBlock *operator()(const sleigh::Exit &exit) {
|
||||
return std::visit(*this, exit);
|
||||
}
|
||||
|
||||
llvm::BasicBlock *operator()(const sleigh::InstrExit &exit) {
|
||||
return lifter.exit_block;
|
||||
};
|
||||
|
||||
llvm::BasicBlock *operator()(const sleigh::IntrainstructionIndex &exit) {
|
||||
return lifter.GetOrCreateBlock(exit.target_block_index);
|
||||
};
|
||||
};
|
||||
struct VisitBlockExitTrue : public VisitExit {
|
||||
using VisitExit::operator();
|
||||
|
||||
llvm::BasicBlock *operator()(const sleigh::ConditionalExit &exit) {
|
||||
return std::visit(*this, exit.true_branch);
|
||||
}
|
||||
};
|
||||
|
||||
struct VisitBlockExitFalse : public VisitExit {
|
||||
using VisitExit::operator();
|
||||
|
||||
llvm::BasicBlock *operator()(const sleigh::ConditionalExit &exit) {
|
||||
return std::visit(*this, exit.false_branch);
|
||||
}
|
||||
};
|
||||
|
||||
llvm::BasicBlock *GetTrueOut() {
|
||||
return std::visit(VisitBlockExitTrue{*this}, this->pcode_block->block_exit);
|
||||
}
|
||||
|
||||
llvm::BasicBlock *GetFalseOut() {
|
||||
return std::visit(VisitBlockExitFalse{*this},
|
||||
this->pcode_block->block_exit);
|
||||
}
|
||||
|
||||
LiftStatus TerminateBlockWithCondition(llvm::Value *condition) {
|
||||
llvm::IRBuilder<> ir(this->target_block);
|
||||
this->target_block = llvm::BasicBlock::Create(
|
||||
this->context, "continuation", this->target_block->getParent());
|
||||
ir.CreateCondBr(condition, this->exit_block, this->target_block);
|
||||
if (this->target_block->getTerminator() == nullptr) {
|
||||
llvm::IRBuilder<> ir(this->target_block);
|
||||
|
||||
ir.CreateCondBr(condition, GetTrueOut(), GetFalseOut());
|
||||
}
|
||||
return LiftStatus::kLiftedInstruction;
|
||||
}
|
||||
|
||||
void TerminateBlock() {
|
||||
if (this->target_block->getTerminator() == nullptr) {
|
||||
llvm::IRBuilder ir(this->target_block);
|
||||
ir.CreateBr(this->exit_block);
|
||||
ir.CreateBr(GetTrueOut());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LiftStatus LiftCBranch(llvm::IRBuilder<> &bldr, VarnodeData *outvar,
|
||||
VarnodeData lhs, VarnodeData rhs) {
|
||||
LiftStatus LiftCBranch(llvm::IRBuilder<> &bldr,
|
||||
std::optional<VarnodeData> outvar, VarnodeData lhs,
|
||||
VarnodeData rhs) {
|
||||
auto should_branch = this->LiftInParam(
|
||||
bldr, rhs, llvm::IntegerType::get(this->context, rhs.size * 8));
|
||||
|
||||
@@ -726,40 +843,36 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
return LiftStatus::kLiftedUnsupportedInstruction;
|
||||
}
|
||||
|
||||
if (isVarnodeInConstantSpace(lhs)) {
|
||||
DLOG(ERROR) << "Internal control flow not supported";
|
||||
return LiftStatus::kLiftedUnsupportedInstruction;
|
||||
}
|
||||
|
||||
// directs dont read the address of the variable, the offset is the jump
|
||||
// TODO(Ian): handle other address spaces
|
||||
auto jump_addr = this->replacement_cont.LiftOffsetOrReplace(
|
||||
bldr, lhs, llvm::IntegerType::get(this->context, lhs.size * 8));
|
||||
|
||||
// TODO(Ian): this should probably technically be != 0
|
||||
auto trunc_should_branch = bldr.CreateTrunc(
|
||||
*should_branch, llvm::IntegerType::get(this->context, 1));
|
||||
if (!isVarnodeInConstantSpace(lhs)) {
|
||||
// directs dont read the address of the variable, the offset is the jump
|
||||
// TODO(Ian): handle other address spaces
|
||||
auto jump_addr = this->replacement_cont.LiftOffsetOrReplace(
|
||||
bldr, lhs, this->insn_lifter_parent.GetWordType());
|
||||
|
||||
auto pc_reg_param = this->LiftNormalRegister(bldr, "PC");
|
||||
CHECK(pc_reg_param.has_value());
|
||||
auto pc_reg_ptr = *pc_reg_param;
|
||||
auto orig_pc_value = pc_reg_ptr->LiftAsInParam(
|
||||
bldr, this->insn_lifter_parent.GetWordType());
|
||||
|
||||
auto pc_reg_param = this->LiftNormalRegister(bldr, "PC");
|
||||
assert(pc_reg_param.has_value());
|
||||
auto pc_reg_ptr = *pc_reg_param;
|
||||
auto orig_pc_value =
|
||||
pc_reg_ptr->LiftAsInParam(bldr, this->insn_lifter_parent.GetWordType());
|
||||
if (orig_pc_value.has_value()) {
|
||||
auto next_pc_value =
|
||||
bldr.CreateSelect(trunc_should_branch, jump_addr, *orig_pc_value);
|
||||
|
||||
if (orig_pc_value.has_value()) {
|
||||
auto next_pc_value =
|
||||
bldr.CreateSelect(trunc_should_branch, jump_addr, *orig_pc_value);
|
||||
|
||||
bldr.CreateStore(next_pc_value, this->GetNextPcRef());
|
||||
bldr.CreateStore(next_pc_value, this->GetNextPcRef());
|
||||
}
|
||||
}
|
||||
|
||||
return this->TerminateBlockWithCondition(trunc_should_branch);
|
||||
}
|
||||
|
||||
LiftStatus LiftIntegerBinOp(llvm::IRBuilder<> &bldr, OpCode opc,
|
||||
VarnodeData *outvar, VarnodeData lhs,
|
||||
VarnodeData rhs) {
|
||||
std::optional<VarnodeData> outvar,
|
||||
VarnodeData lhs, VarnodeData rhs) {
|
||||
|
||||
|
||||
if (INTEGER_BINARY_OPS.find(opc) != INTEGER_BINARY_OPS.end()) {
|
||||
@@ -767,6 +880,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
auto lifted_lhs = this->LiftIntegerInParam(bldr, lhs);
|
||||
auto lifted_rhs = this->LiftIntegerInParam(bldr, rhs);
|
||||
if (lifted_lhs.has_value() && lifted_rhs.has_value()) {
|
||||
DLOG(INFO) << "Binop op: " << get_opname(opc);
|
||||
DLOG(INFO) << "Binop with lhs: "
|
||||
<< remill::LLVMThingToString(*lifted_lhs);
|
||||
DLOG(INFO) << "Binop with rhs: "
|
||||
@@ -790,7 +904,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
|
||||
|
||||
LiftStatus LiftBoolBinOp(llvm::IRBuilder<> &bldr, OpCode opc,
|
||||
VarnodeData *outvar, VarnodeData lhs,
|
||||
std::optional<VarnodeData> outvar, VarnodeData lhs,
|
||||
VarnodeData rhs) {
|
||||
|
||||
// We make sure to only attempt to lift params for operands where we know they are booleans
|
||||
@@ -910,7 +1024,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
|
||||
LiftStatus LiftFloatBinOp(llvm::IRBuilder<> &bldr, OpCode opc,
|
||||
VarnodeData *outvar, VarnodeData lhs,
|
||||
std::optional<VarnodeData> outvar, VarnodeData lhs,
|
||||
VarnodeData rhs) {
|
||||
std::optional<BinaryOperator> op_func = this->FindFloatBinOpFunc(opc);
|
||||
if (!op_func) {
|
||||
@@ -936,8 +1050,9 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
|
||||
|
||||
LiftStatus LiftBinOp(llvm::IRBuilder<> &bldr, OpCode opc, VarnodeData *outvar,
|
||||
VarnodeData lhs, VarnodeData rhs) {
|
||||
LiftStatus LiftBinOp(llvm::IRBuilder<> &bldr, OpCode opc,
|
||||
std::optional<VarnodeData> outvar, VarnodeData lhs,
|
||||
VarnodeData rhs) {
|
||||
|
||||
if (opc == OpCode::CPUI_CBRANCH) {
|
||||
return this->LiftCBranch(bldr, outvar, lhs, rhs);
|
||||
@@ -982,7 +1097,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
|
||||
if (opc == OpCode::CPUI_PIECE && outvar) {
|
||||
assert(rhs.size + lhs.size == outvar->size);
|
||||
CHECK(rhs.size + lhs.size == outvar->size);
|
||||
|
||||
// Treat them as integers
|
||||
auto lifted_lhs = this->LiftInParam(
|
||||
@@ -992,14 +1107,14 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
|
||||
if (lifted_lhs.has_value() && lifted_rhs.has_value()) {
|
||||
// Widen the most significant operand and then left shift it to make room for the least significant operand.
|
||||
auto *ms_operand = bldr.CreateZExt(
|
||||
auto ms_operand = bldr.CreateZExt(
|
||||
*lifted_lhs, llvm::IntegerType::get(this->context, outvar->size));
|
||||
auto *shifted_ms_operand = bldr.CreateShl(
|
||||
auto shifted_ms_operand = bldr.CreateShl(
|
||||
ms_operand, llvm::ConstantInt::get(
|
||||
llvm::Type::getInt8Ty(this->context), rhs.size));
|
||||
|
||||
// Now concatenate them with an OR.
|
||||
auto *concat = bldr.CreateOr(shifted_ms_operand, *lifted_rhs);
|
||||
auto concat = bldr.CreateOr(shifted_ms_operand, *lifted_rhs);
|
||||
return this->LiftStoreIntoOutParam(bldr, concat, outvar);
|
||||
}
|
||||
}
|
||||
@@ -1010,15 +1125,13 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
|
||||
if (lifted_lhs.has_value()) {
|
||||
DLOG(INFO) << "SUBPIECE: " << remill::LLVMThingToString(*lifted_lhs);
|
||||
auto new_size = lhs.size - rhs.offset;
|
||||
auto *subpiece_lhs = bldr.CreateTrunc(
|
||||
*lifted_lhs, llvm::IntegerType::get(this->context, new_size * 8));
|
||||
auto subpiece_lhs = bldr.CreateLShr(*lifted_lhs, rhs.offset * 8);
|
||||
|
||||
if (new_size < outvar->size) {
|
||||
if (lhs.size < outvar->size) {
|
||||
subpiece_lhs = bldr.CreateZExt(
|
||||
subpiece_lhs,
|
||||
llvm::IntegerType::get(this->context, 8 * outvar->size));
|
||||
} else if (new_size > outvar->size) {
|
||||
} else if (lhs.size > outvar->size) {
|
||||
subpiece_lhs = bldr.CreateTrunc(
|
||||
subpiece_lhs,
|
||||
llvm::IntegerType::get(this->context, 8 * outvar->size));
|
||||
@@ -1044,8 +1157,9 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
|
||||
LiftStatus LiftThreeOperandOp(llvm::IRBuilder<> &bldr, OpCode opc,
|
||||
VarnodeData *outvar, VarnodeData param0,
|
||||
VarnodeData param1, VarnodeData param2) {
|
||||
std::optional<VarnodeData> outvar,
|
||||
VarnodeData param0, VarnodeData param1,
|
||||
VarnodeData param2) {
|
||||
switch (opc) {
|
||||
case OpCode::CPUI_STORE: {
|
||||
auto addr_operand = param1;
|
||||
@@ -1068,7 +1182,7 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
auto lifted_addr = this->LiftInParam(
|
||||
bldr, param0, this->insn_lifter_parent.GetWordType()),
|
||||
lifted_index = this->LiftIntegerInParam(bldr, param1);
|
||||
auto *elem_size = llvm::ConstantInt::get(
|
||||
auto elem_size = llvm::ConstantInt::get(
|
||||
llvm::IntegerType::get(this->context, param2.size * 8),
|
||||
param2.offset);
|
||||
if (lifted_addr.has_value() && lifted_index.has_value()) {
|
||||
@@ -1095,8 +1209,8 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
|
||||
LiftStatus LiftVariadicOp(llvm::IRBuilder<> &bldr, OpCode opc,
|
||||
VarnodeData *outvar, VarnodeData *vars,
|
||||
int4 isize) {
|
||||
std::optional<VarnodeData> outvar,
|
||||
VarnodeData *vars, int4 isize) {
|
||||
switch (opc) {
|
||||
// We shouldnt encounter this afaik MULTIEQUAL is a decompiler concept?
|
||||
case OpCode::CPUI_MULTIEQUAL: {
|
||||
@@ -1137,7 +1251,8 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
|
||||
static const size_t kEqualityClaimArity = 3;
|
||||
LiftStatus HandleCallOther(llvm::IRBuilder<> &bldr, VarnodeData *outvar,
|
||||
LiftStatus HandleCallOther(llvm::IRBuilder<> &bldr,
|
||||
std::optional<VarnodeData> outvar,
|
||||
VarnodeData *vars, int4 isize) {
|
||||
auto other_func_name = this->GetOtherFuncName(vars, isize);
|
||||
if (other_func_name.has_value()) {
|
||||
@@ -1188,8 +1303,9 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
}
|
||||
|
||||
void LiftPcodeOp(llvm::IRBuilder<> &bldr, OpCode opc, VarnodeData *outvar,
|
||||
VarnodeData *vars, int4 isize) {
|
||||
void LiftPcodeOp(llvm::IRBuilder<> &bldr, OpCode opc,
|
||||
std::optional<VarnodeData> outvar, VarnodeData *vars,
|
||||
int4 isize) {
|
||||
// The MULTIEQUAL op has variadic operands
|
||||
if (opc == OpCode::CPUI_MULTIEQUAL || opc == OpCode::CPUI_CPOOLREF) {
|
||||
this->UpdateStatus(this->LiftVariadicOp(bldr, opc, outvar, vars, isize),
|
||||
@@ -1225,12 +1341,24 @@ class SleighLifter::PcodeToLLVMEmitIntoBlock : public PcodeEmit {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void dump(const Address &addr, OpCode opc, VarnodeData *outvar,
|
||||
VarnodeData *vars, int4 isize) final override {
|
||||
|
||||
void VisitBlock(const sleigh::PcodeBlock &blk) {
|
||||
this->target_block = GetOrCreateBlock(blk.base_index);
|
||||
this->pcode_block = &blk;
|
||||
llvm::IRBuilder bldr(this->target_block);
|
||||
this->LiftBtakenIfReached(bldr, opc);
|
||||
this->LiftPcodeOp(bldr, opc, outvar, vars, isize);
|
||||
this->curr_id++;
|
||||
|
||||
// we have a problem with block terminators where a cbranch <relative> -> fallthrough, need to either exit to the exit block
|
||||
// or transfer to a block. So really our cfg needs to tell us how to terminate a block
|
||||
// either exit (means real control flow), to block (fake control flow)
|
||||
for (auto pc : blk.ops) {
|
||||
this->LiftPcodeOp(bldr, pc.op, pc.outvar, pc.vars.data(), pc.vars.size());
|
||||
}
|
||||
|
||||
this->TerminateBlock();
|
||||
}
|
||||
|
||||
bool ClaimEqualityUsed() const {
|
||||
return this->replacement_cont.IsEqualityUsed();
|
||||
}
|
||||
|
||||
LiftStatus GetStatus() {
|
||||
@@ -1276,17 +1404,19 @@ std::map<OpCode, SleighLifter::PcodeToLLVMEmitIntoBlock::BinaryOperator>
|
||||
}},
|
||||
{OpCode::CPUI_INT_LEFT,
|
||||
[](llvm::Value *lhs, llvm::Value *rhs, llvm::IRBuilder<> &bldr) {
|
||||
if (lhs->getType() != rhs->getType()) {
|
||||
rhs = bldr.CreateZExtOrTrunc(rhs, lhs->getType());
|
||||
}
|
||||
return bldr.CreateShl(lhs, rhs);
|
||||
return CreatePcodeBitShift(
|
||||
lhs, rhs, bldr,
|
||||
[](llvm::Value *lhs, llvm::Value *rhs, llvm::IRBuilder<> &bldr) {
|
||||
return bldr.CreateShl(lhs, rhs);
|
||||
});
|
||||
}},
|
||||
{OpCode::CPUI_INT_RIGHT,
|
||||
[](llvm::Value *lhs, llvm::Value *rhs, llvm::IRBuilder<> &bldr) {
|
||||
if (lhs->getType() != rhs->getType()) {
|
||||
rhs = bldr.CreateZExtOrTrunc(rhs, lhs->getType());
|
||||
}
|
||||
return bldr.CreateLShr(lhs, rhs);
|
||||
return CreatePcodeBitShift(
|
||||
lhs, rhs, bldr,
|
||||
[](llvm::Value *lhs, llvm::Value *rhs, llvm::IRBuilder<> &bldr) {
|
||||
return bldr.CreateLShr(lhs, rhs);
|
||||
});
|
||||
}},
|
||||
{OpCode::CPUI_INT_SRIGHT,
|
||||
[](llvm::Value *lhs, llvm::Value *rhs, llvm::IRBuilder<> &bldr) {
|
||||
@@ -1378,9 +1508,7 @@ SleighLifter::SleighLifter(const remill::Arch &arch_,
|
||||
: InstructionLifter(&arch_, intrinsics_),
|
||||
sleigh_context(new sleigh::SingleInstructionSleighContext(
|
||||
dec_.GetSLAName(), dec_.GetPSpec())),
|
||||
decoder(dec_) {
|
||||
this->decoder.InitializeSleighContext(*sleigh_context);
|
||||
}
|
||||
decoder(dec_) {}
|
||||
|
||||
|
||||
const std::string_view SleighLifter::kInstructionFunctionPrefix =
|
||||
@@ -1427,10 +1555,11 @@ SleighLifter::DefineInstructionFunction(Instruction &inst,
|
||||
std::pair<LiftStatus, std::optional<llvm::Function *>>
|
||||
SleighLifter::LiftIntoInternalBlockWithSleighState(
|
||||
Instruction &inst, llvm::Module *target_mod, bool is_delayed,
|
||||
const std::optional<sleigh::BranchTakenVar> &btaken) {
|
||||
const sleigh::MaybeBranchTakenVar &btaken,
|
||||
const ContextValues &context_values) {
|
||||
|
||||
this->sleigh_context->resetContext();
|
||||
this->decoder.InitializeSleighContext(*this->sleigh_context);
|
||||
this->decoder.InitializeSleighContext(*this->sleigh_context, context_values);
|
||||
|
||||
sleigh::PcodeDecoder pcode_record(this->GetEngine());
|
||||
sleigh_context->oneInstruction(inst.pc, pcode_record, inst.bytes);
|
||||
@@ -1441,7 +1570,6 @@ SleighLifter::LiftIntoInternalBlockWithSleighState(
|
||||
DLOG(INFO) << "Secondary lift of bytes: " << llvm::toHex(inst.bytes);
|
||||
auto target_func = this->DefineInstructionFunction(inst, target_mod);
|
||||
|
||||
|
||||
llvm::BasicBlock *target_block = &target_func->getEntryBlock();
|
||||
llvm::IRBuilder<> ir(target_block);
|
||||
auto internal_state_pointer =
|
||||
@@ -1461,25 +1589,36 @@ SleighLifter::LiftIntoInternalBlockWithSleighState(
|
||||
//TODO(Ian): make a safe to use sleighinstruction context that wraps a context with an arch to preform reset reinits
|
||||
|
||||
|
||||
auto cfg = sleigh::CreateCFG(pcode_record.ops);
|
||||
|
||||
SleighLifter::PcodeToLLVMEmitIntoBlock lifter(
|
||||
target_block, internal_state_pointer, inst, *this,
|
||||
this->sleigh_context->getUserOpNames(), exit_block, btaken);
|
||||
|
||||
sleigh_context->oneInstruction(inst.pc, lifter, inst.bytes);
|
||||
for (auto blk : cfg.blocks) {
|
||||
lifter.VisitBlock(blk.second);
|
||||
}
|
||||
|
||||
// Log error if claim_eq values that were declared saw no uses
|
||||
if (lifter.ClaimEqualityUsed()) {
|
||||
LOG(ERROR) << "claim_eq value not used when lifting " << inst.Serialize();
|
||||
}
|
||||
|
||||
ir.CreateBr(lifter.GetOrCreateBlock(0));
|
||||
|
||||
lifter.TerminateBlock();
|
||||
|
||||
// Setup like an ISEL
|
||||
SleighLifter::SetISelAttributes(target_func);
|
||||
remill::InitFunctionAttributes(target_func);
|
||||
|
||||
CHECK(remill::VerifyFunction(target_func));
|
||||
return {lifter.GetStatus(), target_func};
|
||||
}
|
||||
|
||||
LiftStatus SleighLifter::LiftIntoBlockWithSleighState(
|
||||
Instruction &inst, llvm::BasicBlock *block, llvm::Value *state_ptr,
|
||||
bool is_delayed, const std::optional<sleigh::BranchTakenVar> &btaken) {
|
||||
bool is_delayed, const sleigh::MaybeBranchTakenVar &btaken,
|
||||
const ContextValues &context_values) {
|
||||
if (!inst.IsValid()) {
|
||||
DLOG(ERROR) << "Invalid function" << inst.Serialize();
|
||||
return kLiftedInvalidInstruction;
|
||||
@@ -1488,7 +1627,7 @@ LiftStatus SleighLifter::LiftIntoBlockWithSleighState(
|
||||
|
||||
// Call the instruction function
|
||||
auto res = this->LiftIntoInternalBlockWithSleighState(
|
||||
inst, block->getModule(), is_delayed, btaken);
|
||||
inst, block->getModule(), is_delayed, btaken, context_values);
|
||||
|
||||
if (res.first != LiftStatus::kLiftedInstruction || !res.second.has_value()) {
|
||||
return res.first;
|
||||
@@ -1546,9 +1685,10 @@ Sleigh &SleighLifter::GetEngine(void) const {
|
||||
}
|
||||
|
||||
SleighLifterWithState::SleighLifterWithState(
|
||||
std::optional<sleigh::BranchTakenVar> btaken_,
|
||||
sleigh::MaybeBranchTakenVar btaken_, ContextValues context_values_,
|
||||
std::shared_ptr<SleighLifter> lifter_)
|
||||
: btaken(btaken_),
|
||||
context_values(std::move(context_values_)),
|
||||
lifter(std::move(lifter_)) {}
|
||||
|
||||
// Lift a single instruction into a basic block. `is_delayed` signifies that
|
||||
@@ -1556,8 +1696,8 @@ SleighLifterWithState::SleighLifterWithState(
|
||||
LiftStatus
|
||||
SleighLifterWithState::LiftIntoBlock(Instruction &inst, llvm::BasicBlock *block,
|
||||
llvm::Value *state_ptr, bool is_delayed) {
|
||||
return this->lifter->LiftIntoBlockWithSleighState(inst, block, state_ptr,
|
||||
is_delayed, this->btaken);
|
||||
return this->lifter->LiftIntoBlockWithSleighState(
|
||||
inst, block, state_ptr, is_delayed, this->btaken, this->context_values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -443,6 +443,27 @@ bool VerifyModule(llvm::Module *module) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<std::string> VerifyFunctionMsg(llvm::Function *func) {
|
||||
std::string error;
|
||||
llvm::raw_string_ostream error_stream(error);
|
||||
if (llvm::verifyFunction(*func, &error_stream)) {
|
||||
error_stream.flush();
|
||||
return error;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Try to verify a function.
|
||||
bool VerifyFunction(llvm::Function *func) {
|
||||
if (auto error = VerifyFunctionMsg(func)) {
|
||||
DLOG(ERROR) << "Error verifying function: " << *error;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<llvm::Module>
|
||||
LoadModuleFromFile(llvm::LLVMContext *context,
|
||||
std::filesystem::path file_name) {
|
||||
@@ -556,6 +577,12 @@ namespace {
|
||||
# define REMILL_BUILD_SEMANTICS_DIR_SPARC64
|
||||
#endif // REMILL_BUILD_SEMANTICS_DIR_SPARC64
|
||||
|
||||
#ifndef REMILL_BUILD_SEMANTICS_DIR_PPC64_32ADDR
|
||||
# error \
|
||||
"Macro `REMILL_BUILD_SEMANTICS_DIR_PPC64_32ADDR` must be defined to support the PPC64_32ADDR architectures."
|
||||
# define REMILL_BUILD_SEMANTICS_DIR_PPC64_32ADDR
|
||||
#endif // REMILL_BUILD_SEMANTICS_DIR_PPC64_32ADDR
|
||||
|
||||
#ifndef REMILL_INSTALL_SEMANTICS_DIR
|
||||
# error "Macro `REMILL_INSTALL_SEMANTICS_DIR` must be defined."
|
||||
# define REMILL_INSTALL_SEMANTICS_DIR
|
||||
@@ -574,6 +601,7 @@ const paths_t &DefaultSemanticsSearchPaths() {
|
||||
REMILL_BUILD_SEMANTICS_DIR_AARCH64,
|
||||
REMILL_BUILD_SEMANTICS_DIR_SPARC32,
|
||||
REMILL_BUILD_SEMANTICS_DIR_SPARC64,
|
||||
REMILL_BUILD_SEMANTICS_DIR_PPC64_32ADDR,
|
||||
REMILL_INSTALL_SEMANTICS_DIR,
|
||||
"/usr/local/share/remill/" MAJOR_MINOR "/semantics",
|
||||
"/usr/share/remill/" MAJOR_MINOR "/semantics",
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
From bd02af97c29961dc6544cccdee57526c3812dc15 Mon Sep 17 00:00:00 2001
|
||||
From: William Tan <1284324+Ninja3047@users.noreply.github.com>
|
||||
Date: Mon, 19 Dec 2022 16:29:09 -0500
|
||||
Subject: [PATCH] ppc_common
|
||||
|
||||
---
|
||||
.../PowerPC/data/languages/ppc_common.sinc | 21 +++++++++++++++++--
|
||||
1 file changed, 19 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Processors/PowerPC/data/languages/ppc_common.sinc b/Ghidra/Processors/PowerPC/data/languages/ppc_common.sinc
|
||||
index 8498e4bdc..761a2e9a0 100644
|
||||
--- a/Ghidra/Processors/PowerPC/data/languages/ppc_common.sinc
|
||||
+++ b/Ghidra/Processors/PowerPC/data/languages/ppc_common.sinc
|
||||
@@ -3,6 +3,10 @@
|
||||
# version 1.0
|
||||
|
||||
define endian=$(ENDIAN);
|
||||
+@define INST_NEXT_PTR "pc"
|
||||
+
|
||||
+define pcodeop claim_eq;
|
||||
+
|
||||
|
||||
define alignment=2;
|
||||
|
||||
@@ -1200,6 +1204,9 @@ attach names [ vrDD vrAD vrBD vrSD vrCD]
|
||||
[ v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15
|
||||
v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30 v31 ];
|
||||
|
||||
+
|
||||
+remill_insn_size: calculated_size is epsilon [calculated_size= inst_next-inst_start; ] { local insn_size_hinted:$(REGISTER_SIZE)=calculated_size;
|
||||
+ export insn_size_hinted; }
|
||||
vrD: vrDD is vrDD & vrDR { export vrDR; }
|
||||
vrA: vrAD is vrAD & vrAR { export vrAR; }
|
||||
vrB: vrBD is vrBD & vrBR { export vrBR; }
|
||||
@@ -1825,9 +1832,19 @@ macro unpackFPSCR(tmp) {
|
||||
REL_ABS: "a" is AA = 1 {}
|
||||
REL_ABS: is AA = 0 {}
|
||||
|
||||
-addressLI: reloc is LI & AA=0 [ reloc = inst_start + LI*4;] { export *[ram]:4 reloc; }
|
||||
+addressLI: reloc is LI & AA=0 ; remill_insn_size [ reloc = inst_start + LI*4;] {
|
||||
+remill_please_dont_use_this_temp_named:$(REGISTER_SIZE)=reloc;
|
||||
+claim_eq(remill_please_dont_use_this_temp_named, ($(INST_NEXT_PTR)-remill_insn_size) + LI*4);
|
||||
+ export *[ram]:4 reloc;
|
||||
+ }
|
||||
+
|
||||
addressLI: reloc is LI & AA=1 [ reloc = LI*4; ] { export *[ram]:4 reloc; }
|
||||
-addressBD: reloc is BD & AA=0 [ reloc = inst_start + BD*4; ] { export *[ram]:4 reloc; }
|
||||
+addressBD: reloc is BD & AA=0 ; remill_insn_size [ reloc = inst_start + BD*4; ] {
|
||||
+remill_please_dont_use_this_temp_name12:$(REGISTER_SIZE)=reloc;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name12, ($(INST_NEXT_PTR)-remill_insn_size) + BD*4);
|
||||
+ export *[ram]:4 reloc;
|
||||
+ }
|
||||
+
|
||||
addressBD: reloc is BD & AA=1 [ reloc = BD*4; ] { export *[ram]:4 reloc; }
|
||||
|
||||
OFF16SH: val is D0 & D1 & D2 [ val = ((D0 << 6) | (D1 << 1) | D2) << 16; ] { export *[const]:4 val;}
|
||||
--
|
||||
2.37.1 (Apple Git-137.1)
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
From e6fbe2b39e45a78af3031a0357eef199dedc342c Mon Sep 17 00:00:00 2001
|
||||
From: William Tan <1284324+Ninja3047@users.noreply.github.com>
|
||||
Date: Mon, 19 Dec 2022 16:29:10 -0500
|
||||
Subject: [PATCH] ppc_instructions
|
||||
|
||||
---
|
||||
.../data/languages/ppc_instructions.sinc | 102 ++++++++++++++----
|
||||
1 file changed, 80 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Processors/PowerPC/data/languages/ppc_instructions.sinc b/Ghidra/Processors/PowerPC/data/languages/ppc_instructions.sinc
|
||||
index a3fa7a8f7..75aa75f58 100644
|
||||
--- a/Ghidra/Processors/PowerPC/data/languages/ppc_instructions.sinc
|
||||
+++ b/Ghidra/Processors/PowerPC/data/languages/ppc_instructions.sinc
|
||||
@@ -368,10 +368,15 @@
|
||||
# special case when branch is to fall-through instruction, just loading the link register
|
||||
#bl (Load LR)
|
||||
:bl addressBD is $(NOTVLE) & OP=16 & addressBD & REL_ABS & BO_0=1 & BO_2=1 & BD=1 & LK=1
|
||||
-{
|
||||
+ {
|
||||
+remill_please_dont_use_this_temp_name45:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name45, $(INST_NEXT_PTR));
|
||||
LR = inst_next;
|
||||
+
|
||||
goto addressBD;
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
|
||||
|
||||
@@ -493,15 +498,25 @@
|
||||
|
||||
#bgectr 0x4c 80 04 20
|
||||
:b^CC^"ctr" is $(NOTVLE) & OP=19 & CC & BO_0=0 & BO_2=1 & BI_CR= 0 & BH=0 & LK=0 & BITS_13_15=0 & XOP_1_10=528
|
||||
-{
|
||||
- if (!CC) goto inst_next;
|
||||
+ {
|
||||
+remill_please_dont_use_this_temp_name4f:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name4f, $(INST_NEXT_PTR));
|
||||
+ if (!CC) goto inst_next;
|
||||
+
|
||||
goto [CTR];
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
:b^CC^"ctr" BH is $(NOTVLE) & OP=19 & CC & BO_0=0 & BO_2=1 & BI_CR= 0 & BH & BH_BITS!=0 & LK=0 & BITS_13_15=0 & XOP_1_10=528
|
||||
-{
|
||||
- if (!CC) goto inst_next;
|
||||
+ {
|
||||
+remill_please_dont_use_this_temp_name51:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name51, $(INST_NEXT_PTR));
|
||||
+ if (!CC) goto inst_next;
|
||||
+
|
||||
goto [CTR];
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
#bgectrl 0x4c 80 04 21
|
||||
:b^CC^"ctrl" is $(NOTVLE) & OP=19 & CC & BO_0=0 & BO_2=1 & BI_CR= 0 & BH=0 & LK=1 & BITS_13_15=0 & XOP_1_10=528
|
||||
@@ -521,17 +536,27 @@
|
||||
|
||||
#bgectr cr3 0x4c 8c 04 20
|
||||
:b^CC^"ctr" BI_CR is $(NOTVLE) & OP=19 & CC & BI_CR & BO_0=0 & BO_2=1 & BH=0 & LK=0 & BITS_13_15=0 & XOP_1_10=528
|
||||
-{
|
||||
- if (!CC) goto inst_next;
|
||||
+ {
|
||||
+remill_please_dont_use_this_temp_name53:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name53, $(INST_NEXT_PTR));
|
||||
+ if (!CC) goto inst_next;
|
||||
+
|
||||
goto [CTR];
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
#bnectr cr2,#0x3 0x4c 8c 1c 20
|
||||
:b^CC^"ctr" BI_CR,BH is $(NOTVLE) & OP=19 & CC & BI_CR & BO_0=0 & BO_2=1 & BH & LK=0 & BITS_13_15=0 & XOP_1_10=528
|
||||
-{
|
||||
- if (!CC) goto inst_next;
|
||||
+ {
|
||||
+remill_please_dont_use_this_temp_name55:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name55, $(INST_NEXT_PTR));
|
||||
+ if (!CC) goto inst_next;
|
||||
+
|
||||
goto [CTR];
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
#bgectrl cr2,LAB_xxxx 0x4c 8c 04 21
|
||||
:b^CC^"ctrl" BI_CR is $(NOTVLE) & OP=19 & CC & BI_CR & BO_0=0 & BO_2=1 & BH=0 & LK=1 & BITS_13_15=0 & XOP_1_10=528
|
||||
@@ -1913,26 +1938,45 @@
|
||||
|
||||
#fsel f0r,fr0,fr0,fr0 0xfc 00 00 2e
|
||||
:fsel fD,fA,fC,fB is $(NOTVLE) & OP=63 & fD & fA & fB & fC & XOP_1_5=23 & Rc=0
|
||||
-{
|
||||
+ {
|
||||
local tmpfA = fA;
|
||||
+
|
||||
local tmpfB = fB;
|
||||
+
|
||||
zero:4=0;
|
||||
+
|
||||
fD=fC;
|
||||
+remill_please_dont_use_this_temp_nameef:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_nameef, $(INST_NEXT_PTR));
|
||||
+
|
||||
if (tmpfA f> int2float(zero)) goto inst_next;
|
||||
+
|
||||
fD=tmpfB;
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
#fsel. fr0,fr0,fr0,fr0 0xfc 00 00 2f
|
||||
:fsel. fD,fA,fC,fB is $(NOTVLE) & OP=63 & fD & fA & fB & fC & XOP_1_5=23 & Rc=1
|
||||
-{
|
||||
+ {
|
||||
local tmpfA = fA;
|
||||
+
|
||||
local tmpfB = fB;
|
||||
+
|
||||
zero:4=0;
|
||||
+
|
||||
fD=fC;
|
||||
+
|
||||
cr1flags();
|
||||
+remill_please_dont_use_this_temp_namef1:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_namef1, $(INST_NEXT_PTR));
|
||||
+
|
||||
if (tmpfA f> int2float(zero)) goto inst_next;
|
||||
+
|
||||
fD=tmpfB;
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
#fsqrt f0r,fr0 0xfc 00 00 2c
|
||||
:fsqrt fD,fB is $(NOTVLE) & OP=63 & fD & BITS_16_20=0 & fB & BITS_6_10=0 & XOP_1_5=22 & Rc=0
|
||||
@@ -3960,13 +4004,20 @@ CRM_val: crmval is CRM [crmval = CRM+0;] {export *[const]:1 crmval;}
|
||||
|
||||
#stdcx. r0,8(0) 0x7c 00 01 AD
|
||||
:stdcx. S,RA_OR_ZERO,B is OP=31 & S & RA_OR_ZERO & B & XOP_1_10=214 & BIT_0=1
|
||||
-{
|
||||
+ {
|
||||
EA:$(REGISTER_SIZE) = RA_OR_ZERO + B;
|
||||
+remill_please_dont_use_this_temp_name267:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name267, $(INST_NEXT_PTR));
|
||||
+
|
||||
if (RESERVE == 0) goto inst_next;
|
||||
+
|
||||
*[ram]:8 EA = storeDoubleWordConditionalIndexed(S,RA_OR_ZERO,B);
|
||||
+
|
||||
# set when a stwcx. or stdcx. successfully completes
|
||||
cr0flags(0:$(REGISTER_SIZE));
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
#stdu r0,8(0) 0xf8 00 00 01
|
||||
#stdu r0,8(r2) 0xf8 02 00 01
|
||||
@@ -4150,13 +4201,20 @@ define pcodeop stswxOp;
|
||||
|
||||
#stwcx. r0,8(0) 0x7c 00 01 2D
|
||||
:stwcx. S,RA_OR_ZERO,B is OP=31 & S & RA_OR_ZERO & B & XOP_1_10=150 & BIT_0=1
|
||||
-{
|
||||
+ {
|
||||
EA:$(REGISTER_SIZE) = RA_OR_ZERO + B;
|
||||
+remill_please_dont_use_this_temp_name28d:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name28d, $(INST_NEXT_PTR));
|
||||
+
|
||||
if (RESERVE == 0) goto inst_next;
|
||||
+
|
||||
*[ram]:4 EA = storeWordConditionalIndexed(S,RA_OR_ZERO,B);
|
||||
+
|
||||
# set when a stwcx. or stdcx. successfully completes
|
||||
cr0flags(0:$(REGISTER_SIZE));
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
#stwu r0,r0 0x94 00 00 00
|
||||
:stwu S,dPlusRaAddress is $(NOTVLE) & OP=37 & S & A & dPlusRaAddress
|
||||
--
|
||||
2.37.1 (Apple Git-137.1)
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
From 9bf1ba10175be998e5406fbcde7faec0fddf11f9 Mon Sep 17 00:00:00 2001
|
||||
From: William Tan <1284324+Ninja3047@users.noreply.github.com>
|
||||
Date: Mon, 19 Dec 2022 16:29:10 -0500
|
||||
Subject: [PATCH] ppc_isa
|
||||
|
||||
---
|
||||
.../PowerPC/data/languages/ppc_isa.sinc | 52 ++++++++++++++-----
|
||||
1 file changed, 38 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Processors/PowerPC/data/languages/ppc_isa.sinc b/Ghidra/Processors/PowerPC/data/languages/ppc_isa.sinc
|
||||
index 10f0cc691..ffc603048 100644
|
||||
--- a/Ghidra/Processors/PowerPC/data/languages/ppc_isa.sinc
|
||||
+++ b/Ghidra/Processors/PowerPC/data/languages/ppc_isa.sinc
|
||||
@@ -2377,15 +2377,25 @@ define pcodeop InstructionCacheBlockLockSetX;
|
||||
}
|
||||
|
||||
:b^CC^"ctar" is $(NOTVLE) & OP=19 & CC & BO_0=0 & BO_2=1 & BI_CR= 0 & BH=0 & LK=0 & BITS_13_15=0 & XOP_1_10=560
|
||||
-{
|
||||
- if (!CC) goto inst_next;
|
||||
+ {
|
||||
+remill_please_dont_use_this_temp_name191:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name191, $(INST_NEXT_PTR));
|
||||
+ if (!CC) goto inst_next;
|
||||
+
|
||||
goto [TAR];
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
:b^CC^"ctar" BH is $(NOTVLE) & OP=19 & CC & BO_0=0 & BO_2=1 & BI_CR= 0 & BH & BH_BITS!=0 & LK=0 & BITS_13_15=0 & XOP_1_10=560
|
||||
-{
|
||||
- if (!CC) goto inst_next;
|
||||
+ {
|
||||
+remill_please_dont_use_this_temp_name193:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name193, $(INST_NEXT_PTR));
|
||||
+ if (!CC) goto inst_next;
|
||||
+
|
||||
goto [TAR];
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
:b^CC^"ctarl" is $(NOTVLE) & OP=19 & CC & BO_0=0 & BO_2=1 & BI_CR= 0 & BH=0 & LK=1 & BITS_13_15=0 & XOP_1_10=560
|
||||
[ linkreg=0; globalset(inst_start,linkreg); ]
|
||||
@@ -2403,16 +2413,26 @@ define pcodeop InstructionCacheBlockLockSetX;
|
||||
}
|
||||
|
||||
:b^CC^"ctar" BI_CR is $(NOTVLE) & OP=19 & CC & BI_CR & BO_0=0 & BO_2=1 & BH=0 & LK=0 & BITS_13_15=0 & XOP_1_10=560
|
||||
-{
|
||||
- if (!CC) goto inst_next;
|
||||
+ {
|
||||
+remill_please_dont_use_this_temp_name195:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name195, $(INST_NEXT_PTR));
|
||||
+ if (!CC) goto inst_next;
|
||||
+
|
||||
goto [TAR];
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
:b^CC^"ctar" BI_CR,BH is $(NOTVLE) & OP=19 & CC & BI_CR & BO_0=0 & BO_2=1 & BH & LK=0 & BITS_13_15=0 & XOP_1_10=560
|
||||
-{
|
||||
- if (!CC) goto inst_next;
|
||||
+ {
|
||||
+remill_please_dont_use_this_temp_name197:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name197, $(INST_NEXT_PTR));
|
||||
+ if (!CC) goto inst_next;
|
||||
+
|
||||
goto [TAR];
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
:b^CC^"ctarl" BI_CR is $(NOTVLE) & OP=19 & CC & BI_CR & BO_0=0 & BO_2=1 & BH=0 & LK=1 & BITS_13_15=0 & XOP_1_10=560
|
||||
[ linkreg=0; globalset(inst_start,linkreg); ]
|
||||
@@ -2530,9 +2550,13 @@ define pcodeop InstructionCacheBlockLockSetX;
|
||||
#######################
|
||||
# v3.0
|
||||
|
||||
-:addpcis D,OFF16SH is $(NOTVLE) & OP=19 & XOP_1_5=2 & D & OFF16SH {
|
||||
+:addpcis D,OFF16SH is $(NOTVLE) & OP=19 & XOP_1_5=2 & D & OFF16SH {
|
||||
+remill_please_dont_use_this_temp_name1bd:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name1bd, $(INST_NEXT_PTR));
|
||||
D = inst_next + sext(OFF16SH);
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
:cmpeqb CRFD,A,B is $(NOTVLE) & OP=31 & BITS_21_22=0 & BIT_0=0 & XOP_1_10=224 & A & B & CRFD {
|
||||
tmpa:1 = A:1;
|
||||
--
|
||||
2.37.1 (Apple Git-137.1)
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
From f84f07be17cd7594d32f49f2c3095e65d6f3c393 Mon Sep 17 00:00:00 2001
|
||||
From: William Tan <1284324+Ninja3047@users.noreply.github.com>
|
||||
Date: Mon, 19 Dec 2022 16:29:10 -0500
|
||||
Subject: [PATCH] ppc_vle
|
||||
|
||||
---
|
||||
.../PowerPC/data/languages/ppc_vle.sinc | 68 +++++++++++++++----
|
||||
1 file changed, 55 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Processors/PowerPC/data/languages/ppc_vle.sinc b/Ghidra/Processors/PowerPC/data/languages/ppc_vle.sinc
|
||||
index 3b6283470..b6d763bd0 100644
|
||||
--- a/Ghidra/Processors/PowerPC/data/languages/ppc_vle.sinc
|
||||
+++ b/Ghidra/Processors/PowerPC/data/languages/ppc_vle.sinc
|
||||
@@ -20,9 +20,24 @@ CC32: "ns" is BI_CC_VLE=3 & BO_VLE=0 & BI_CR_VLE & BI_CC_VLE { tmp:1 = 0; getCr
|
||||
CC32: "dnz" is BO_VLE=2 {CTR = CTR-1; tmp:1 = (CTR != 0); export tmp; }
|
||||
CC32: "dz" is BO_VLE=3 {CTR = CTR-1; tmp:1 = (CTR == 0); export tmp; }
|
||||
|
||||
-addrBD8: reloc is BD8_VLE [ reloc = inst_start + (BD8_VLE << 1);] { export *[ram]:4 reloc; }
|
||||
-addrBD15: reloc is BD15_VLE [ reloc = inst_start + (BD15_VLE << 1);] { export *[ram]:4 reloc; }
|
||||
-addrBD24: reloc is BD24_VLE [ reloc = inst_start + (BD24_VLE << 1);] { export *[ram]:4 reloc; }
|
||||
+addrBD8: reloc is BD8_VLE ; remill_insn_size [ reloc = inst_start + (BD8_VLE << 1);] {
|
||||
+remill_please_dont_use_this_temp_name23:$(REGISTER_SIZE)=reloc;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name23, ($(INST_NEXT_PTR)-remill_insn_size) + (BD8_VLE << 1));
|
||||
+ export *[ram]:4 reloc;
|
||||
+ }
|
||||
+
|
||||
+addrBD15: reloc is BD15_VLE ; remill_insn_size [ reloc = inst_start + (BD15_VLE << 1);] {
|
||||
+remill_please_dont_use_this_temp_name26:$(REGISTER_SIZE)=reloc;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name26, ($(INST_NEXT_PTR)-remill_insn_size) + (BD15_VLE << 1));
|
||||
+ export *[ram]:4 reloc;
|
||||
+ }
|
||||
+
|
||||
+addrBD24: reloc is BD24_VLE ; remill_insn_size [ reloc = inst_start + (BD24_VLE << 1);] {
|
||||
+remill_please_dont_use_this_temp_name29:$(REGISTER_SIZE)=reloc;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name29, ($(INST_NEXT_PTR)-remill_insn_size) + (BD24_VLE << 1));
|
||||
+ export *[ram]:4 reloc;
|
||||
+ }
|
||||
+
|
||||
|
||||
d8PlusRaAddress: S8IMM(A) is S8IMM & A {tmp:$(REGISTER_SIZE) = A+S8IMM; export tmp; }
|
||||
d8PlusRaOrZeroAddress: S8IMM(RA_OR_ZERO) is S8IMM & RA_OR_ZERO {tmp:$(REGISTER_SIZE) = RA_OR_ZERO+S8IMM; export tmp; }
|
||||
@@ -53,38 +68,65 @@ IMM16B: val is IMM_0_10_VLE & IMM_16_20_VLE [ val = (IMM_16_20_VLE << 11) |
|
||||
goto addrBD24;
|
||||
}
|
||||
|
||||
-:e_bl addrBD24 is $(ISVLE) & OP=30 & BIT_25=0 & LK=1 & addrBD24 {
|
||||
+:e_bl addrBD24 is $(ISVLE) & OP=30 & BIT_25=0 & LK=1 & addrBD24 {
|
||||
+remill_please_dont_use_this_temp_name3a:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name3a, $(INST_NEXT_PTR));
|
||||
LR = inst_next;
|
||||
+
|
||||
call addrBD24;
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
:se_b addrBD8 is $(ISVLE) & OP6_VLE=58 & BIT9_VLE=0 & LK8_VLE=0 & addrBD8 {
|
||||
goto addrBD8;
|
||||
}
|
||||
|
||||
-:se_bl addrBD8 is $(ISVLE) & OP6_VLE=58 & BIT9_VLE=0 & LK8_VLE=1 & addrBD8 {
|
||||
+:se_bl addrBD8 is $(ISVLE) & OP6_VLE=58 & BIT9_VLE=0 & LK8_VLE=1 & addrBD8 {
|
||||
+remill_please_dont_use_this_temp_name3e:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name3e, $(INST_NEXT_PTR));
|
||||
LR = inst_next;
|
||||
+
|
||||
call addrBD8;
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
# NOTE: For the conditional branches, the "official" mnemonics have just bc and bcl.
|
||||
# We use extended mnemonics so the display is understandable without having to cross-
|
||||
# reference multiple tables.
|
||||
-:e_b^CC32 addrBD15 is $(ISVLE) & OP=30 & XOP_VLE=8 & LK=0 & addrBD15 & CC32 {
|
||||
+:e_b^CC32 addrBD15 is $(ISVLE) & OP=30 & XOP_VLE=8 & LK=0 & addrBD15 & CC32 {
|
||||
+remill_please_dont_use_this_temp_name40:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name40, $(INST_NEXT_PTR));
|
||||
if (CC32 == 0) goto inst_next;
|
||||
+
|
||||
goto addrBD15;
|
||||
-}
|
||||
|
||||
-:e_b^CC32^"l" addrBD15 is $(ISVLE) & OP=30 & XOP_VLE=8 & LK=1 & addrBD15 & CC32 {
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+:e_b^CC32^"l" addrBD15 is $(ISVLE) & OP=30 & XOP_VLE=8 & LK=1 & addrBD15 & CC32 {
|
||||
+remill_please_dont_use_this_temp_name42:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name42, $(INST_NEXT_PTR));
|
||||
if (CC32 == 0) goto inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name42, $(INST_NEXT_PTR));
|
||||
+
|
||||
LR= inst_next;
|
||||
+
|
||||
call [addrBD15];
|
||||
-}
|
||||
|
||||
-:se_b^CC16 addrBD8 is $(ISVLE) & OP5_VLE=28 & addrBD8 & CC16 {
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+:se_b^CC16 addrBD8 is $(ISVLE) & OP5_VLE=28 & addrBD8 & CC16 {
|
||||
+remill_please_dont_use_this_temp_name44:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_name44, $(INST_NEXT_PTR));
|
||||
if (CC16 == 0) goto inst_next;
|
||||
+
|
||||
goto addrBD8;
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
#######
|
||||
|
||||
:se_bctr is $(ISVLE) & OP15_VLE=3 & LK0_VLE=0 {
|
||||
--
|
||||
2.37.1 (Apple Git-137.1)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
From aa64d2d91c4d8cba234533e8ef619d01cbc81ef2 Mon Sep 17 00:00:00 2001
|
||||
From: William Tan <1284324+Ninja3047@users.noreply.github.com>
|
||||
Date: Mon, 19 Dec 2022 16:29:11 -0500
|
||||
Subject: [PATCH] quicciii
|
||||
|
||||
---
|
||||
.../Processors/PowerPC/data/languages/quicciii.sinc | 12 ++++++++++--
|
||||
1 file changed, 10 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/Ghidra/Processors/PowerPC/data/languages/quicciii.sinc b/Ghidra/Processors/PowerPC/data/languages/quicciii.sinc
|
||||
index 8b7977bce..13b381559 100644
|
||||
--- a/Ghidra/Processors/PowerPC/data/languages/quicciii.sinc
|
||||
+++ b/Ghidra/Processors/PowerPC/data/languages/quicciii.sinc
|
||||
@@ -62,13 +62,21 @@ define pcodeop invalidateTLB;
|
||||
}
|
||||
|
||||
:isel^CC_X_OPm D,RA_OR_ZERO,B,CC_X_OP is OP=31 & D & RA_OR_ZERO & B & CC_X_OP & CC_X_OPm & XOP_1_5=15
|
||||
-{
|
||||
+ {
|
||||
local tmp:$(REGISTER_SIZE) = RA_OR_ZERO;
|
||||
+
|
||||
D = B;
|
||||
+remill_please_dont_use_this_temp_namef:$(REGISTER_SIZE)=inst_next;
|
||||
+claim_eq(remill_please_dont_use_this_temp_namef, $(INST_NEXT_PTR));
|
||||
+
|
||||
if (!CC_X_OP) goto inst_next;
|
||||
+
|
||||
D = tmp;
|
||||
+
|
||||
# D = (zext(CC_X_OP) * RA_OR_ZERO) + (zext(!CC_X_OP) * B);
|
||||
-}
|
||||
+
|
||||
+ }
|
||||
+
|
||||
|
||||
@ifndef IS_ISA
|
||||
#mbar 0 #FIXME
|
||||
--
|
||||
2.37.1 (Apple Git-137.1)
|
||||
|
||||
+1
-5
@@ -27,7 +27,7 @@ LLVM_VERSION=llvm-15
|
||||
OS_VERSION=
|
||||
ARCH_VERSION=
|
||||
BUILD_FLAGS=
|
||||
CXX_COMMON_VERSION="0.2.12"
|
||||
CXX_COMMON_VERSION="0.2.22"
|
||||
|
||||
# There are pre-build versions of various libraries for specific
|
||||
# Ubuntu releases.
|
||||
@@ -280,10 +280,6 @@ function Package
|
||||
function GetLLVMVersion
|
||||
{
|
||||
case ${1} in
|
||||
14)
|
||||
LLVM_VERSION=llvm-14
|
||||
return 0
|
||||
;;
|
||||
15)
|
||||
LLVM_VERSION=llvm-15
|
||||
return 0
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
|
||||
V=""
|
||||
case ${LLVM_VERSION} in
|
||||
llvm14*)
|
||||
V=14
|
||||
;;
|
||||
llvm15*)
|
||||
V=15
|
||||
;;
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
Example invocation for x86 `python replace_pc_relative_disactions/main.py <target .sinc file> --out <output_patch_file> --inst_next_size_hint "\$(SIZE)" --pc_def register_definitions/x86.ia`
|
||||
Example invocation for x86 `python replace_pc_relative_disactions/main.py <target .sinc file> --out_dir <output_directory> --inst_next_size_hint "\$(SIZE)" --pc_def register_definitions/x86.ia`
|
||||
Example invocation for PowerPC using `python replace_pc_relative_disactions/main.py <ghidra_dir>/Ghidra/Processors/PowerPC/data/languages/{ppc_common,ppc_embedded,ppc_instructions,ppc_isa,ppc_vle,quicciii}.sinc --pc_def ./register_definitions/powerpc.ia --inst_next_size_hint '$(REGISTER_SIZE)' --base_path <ghidra_dir> --out_dir $PWD/../../patches/sleigh`
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
@define INST_NEXT_PTR "pc"
|
||||
@@ -1,6 +1,7 @@
|
||||
import argparse
|
||||
import re
|
||||
from typing import Dict, List, Match, Optional, Set, Tuple
|
||||
from pathlib import Path
|
||||
import os
|
||||
import tempfile
|
||||
import subprocess
|
||||
@@ -198,23 +199,15 @@ def build_constructor(env: Environment, constructor: Match[str]) -> Optional[str
|
||||
|
||||
ENDIAN_DEF_REGEX = "define\s*endian\s*=[\w()$]+;"
|
||||
|
||||
|
||||
def main():
|
||||
prsr = argparse.ArgumentParser("Disassembly action replacer")
|
||||
prsr.add_argument("target_file")
|
||||
prsr.add_argument("--pc_def", required=True)
|
||||
prsr.add_argument("--inst_next_size_hint", required=True)
|
||||
prsr.add_argument("--base_path", required=True)
|
||||
prsr.add_argument("--commit_message", required=True)
|
||||
prsr.add_argument("--out", required=True)
|
||||
|
||||
args = prsr.parse_args()
|
||||
def generate_patch(target_file, pc_def_path, inst_next_size_hint, base_path, out_dir):
|
||||
print(CONSTRUCTOR_BASE_REGEX)
|
||||
construct_pat = re.compile(CONSTRUCTOR_BASE_REGEX)
|
||||
|
||||
commit_message = f"{Path(target_file).stem}"
|
||||
|
||||
total_output = ""
|
||||
with open(args.target_file, 'r') as target_f:
|
||||
with open(args.pc_def) as pc_def_file:
|
||||
with open(target_file, 'r') as target_f:
|
||||
with open(pc_def_path) as pc_def_file:
|
||||
pc_def = pc_def_file.read()
|
||||
|
||||
target = target_f.read()
|
||||
@@ -225,23 +218,22 @@ def main():
|
||||
if endian_def is not None:
|
||||
total_output += target[0:endian_def.end()]
|
||||
|
||||
# can insert our defs here
|
||||
total_output += "\n" + pc_def
|
||||
total_output += "\ndefine pcodeop claim_eq;\n"
|
||||
# can insert our defs here
|
||||
# we only want to define this once in the file that also defines the endian-ness
|
||||
total_output += "\n" + pc_def
|
||||
total_output += "\ndefine pcodeop claim_eq;\n"
|
||||
|
||||
first_constructor_offset = next(
|
||||
construct_pat.finditer(target)).start()
|
||||
print("first constructor at: " + str(first_constructor_offset))
|
||||
target_insert_match = max(filter(lambda k: k.end() < first_constructor_offset,
|
||||
re.finditer("@endif", target)), key=lambda elem: elem.end(), default=None)
|
||||
|
||||
target_insert_loc = target_insert_match.end(
|
||||
) if target_insert_match else (first_constructor_offset)
|
||||
target_insert_loc = first_constructor_offset
|
||||
|
||||
total_output += target[endian_def.end()
|
||||
if endian_def is not None else 0: target_insert_loc]
|
||||
|
||||
total_output += f"\n{REMILL_INSN_SIZE_NAME}: calculated_size is epsilon [calculated_size= inst_next-inst_start; ] {{ local insn_size_hinted:{args.inst_next_size_hint}=calculated_size; \n export insn_size_hinted; }}\n"
|
||||
if endian_def is not None:
|
||||
# This should be defined once and it MUST be defined after all context definitions
|
||||
total_output += f"\n{REMILL_INSN_SIZE_NAME}: calculated_size is epsilon [calculated_size= inst_next-inst_start; ] {{ local insn_size_hinted:{inst_next_size_hint}=calculated_size; \n export insn_size_hinted; }}\n"
|
||||
|
||||
last_offset = target_insert_loc
|
||||
cont = Context()
|
||||
@@ -249,7 +241,7 @@ def main():
|
||||
total_output += constructor.string[last_offset:constructor.start()]
|
||||
last_offset = constructor.end()
|
||||
env = Environment(cont,
|
||||
args.inst_next_size_hint, [InstStartReplacer(), InstNextReplacer()])
|
||||
inst_next_size_hint, [InstStartReplacer(), InstNextReplacer()])
|
||||
act_section = constructor.group("action_section")
|
||||
if act_section:
|
||||
statements = act_section[
|
||||
@@ -267,22 +259,37 @@ def main():
|
||||
total_output += target[last_offset:]
|
||||
|
||||
# compute the patch header
|
||||
src_and_dst = os.path.relpath(args.target_file, args.base_path)
|
||||
src_and_dst = os.path.relpath(target_file, base_path)
|
||||
|
||||
# compute the patch
|
||||
|
||||
with open(args.target_file, 'w') as target_f:
|
||||
with open(target_file, 'w') as target_f:
|
||||
target_f.write(total_output)
|
||||
|
||||
res = subprocess.run(["git", "commit", "-a", "-m", args.commit_message],
|
||||
cwd=args.base_path, capture_output=True)
|
||||
res = subprocess.run(["git", "commit", "-a", "-m", commit_message],
|
||||
cwd=base_path, capture_output=True)
|
||||
|
||||
print(res)
|
||||
print(subprocess.run(
|
||||
["git", "format-patch", "-1", "HEAD", "-o", args.out], cwd=args.base_path, capture_output=True))
|
||||
["git", "format-patch", "-1", "HEAD", "-o", out_dir], cwd=base_path, capture_output=True))
|
||||
|
||||
subprocess.run(
|
||||
["git", "reset", "--hard", "HEAD~1"], cwd=args.base_path, capture_output=True)
|
||||
["git", "reset", "--hard", "HEAD~1"], cwd=base_path, capture_output=True)
|
||||
|
||||
|
||||
def main():
|
||||
prsr = argparse.ArgumentParser("Disassembly action replacer")
|
||||
prsr.add_argument("target_files", nargs="+", help="List of files to patch")
|
||||
prsr.add_argument("--pc_def", required=True, help="Path to file containing definition of INST_NEXT_PTR")
|
||||
prsr.add_argument("--inst_next_size_hint", required=True, help="Number of bytes of the PC register")
|
||||
prsr.add_argument("--base_path", required=True, help="Path to Ghidra git repo")
|
||||
prsr.add_argument("--out_dir", required=True)
|
||||
|
||||
args = prsr.parse_args()
|
||||
|
||||
for target in args.target_files:
|
||||
print(f"Processing {target}")
|
||||
generate_patch(target, args.pc_def, args.inst_next_size_hint, args.base_path, args.out_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -19,6 +19,7 @@ add_library(
|
||||
STATIC
|
||||
TestRunner.cpp
|
||||
"${TEST_RUNNER_INCLUDE_DIR}/test_runner/TestRunner.h"
|
||||
"${TEST_RUNNER_INCLUDE_DIR}/test_runner/TestOutputSpec.h"
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -121,22 +121,6 @@ std::string MemoryHandler::DumpState() const {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T MemoryHandler::ReadMemory(uint64_t addr) {
|
||||
auto buff = this->readSize(addr, sizeof(T));
|
||||
return llvm::support::endian::read<T>(buff.data(), this->endian);
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
void MemoryHandler::WriteMemory(uint64_t addr, T value) {
|
||||
std::vector<uint8_t> buff(sizeof(T));
|
||||
llvm::support::endian::write<T>(buff.data(), value, this->endian);
|
||||
for (size_t i = 0; i < sizeof(T); i++) {
|
||||
this->state[addr + i] = buff[i];
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<uint64_t, uint8_t> MemoryHandler::GetUninitializedReads() {
|
||||
return this->uninitialized_reads;
|
||||
}
|
||||
@@ -237,12 +221,11 @@ std::unordered_map<TypeId, llvm::Type *> LiftingTester::GetTypeMapping() {
|
||||
|
||||
std::optional<std::pair<llvm::Function *, remill::Instruction>>
|
||||
LiftingTester::LiftInstructionFunction(std::string_view fname,
|
||||
std::string_view bytes,
|
||||
uint64_t address) {
|
||||
std::string_view bytes, uint64_t address,
|
||||
const remill::DecodingContext &ctx) {
|
||||
remill::Instruction insn;
|
||||
// This works for now since each arch has an initial context that represents the arch correctly.
|
||||
if (!this->arch->DecodeInstruction(address, bytes, insn,
|
||||
this->arch->CreateInitialContext())) {
|
||||
if (!this->arch->DecodeInstruction(address, bytes, insn, ctx)) {
|
||||
LOG(ERROR) << "Failed decode";
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -278,6 +261,14 @@ LiftingTester::LiftInstructionFunction(std::string_view fname,
|
||||
return std::make_pair(target_func, insn);
|
||||
}
|
||||
|
||||
std::optional<std::pair<llvm::Function *, remill::Instruction>>
|
||||
LiftingTester::LiftInstructionFunction(std::string_view fname,
|
||||
std::string_view bytes,
|
||||
uint64_t address) {
|
||||
return LiftInstructionFunction(fname, bytes, address,
|
||||
this->arch->CreateInitialContext());
|
||||
}
|
||||
|
||||
const remill::Arch::ArchPtr &LiftingTester::GetArch() const {
|
||||
return this->arch;
|
||||
}
|
||||
@@ -320,4 +311,4 @@ void StubOutFlagComputationInstrinsics(llvm::Module *mod,
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace test_runner
|
||||
} // namespace test_runner
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <remill/Arch/Arch.h>
|
||||
#include <test_runner/TestRunner.h>
|
||||
|
||||
#include <any>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace test_runner {
|
||||
|
||||
using MemoryModifier = std::function<void(MemoryHandler &)>;
|
||||
using RegisterValue = std::variant<uint64_t, uint8_t>;
|
||||
using RegisterValueRef = std::variant<std::reference_wrapper<uint64_t>,
|
||||
std::reference_wrapper<uint8_t>>;
|
||||
|
||||
struct RegisterCondition {
|
||||
std::string register_name;
|
||||
RegisterValue enforced_value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept State = std::is_base_of_v<ArchState, T>;
|
||||
|
||||
template <State S>
|
||||
class TestOutputSpec {
|
||||
public:
|
||||
uint64_t addr;
|
||||
std::string target_bytes;
|
||||
|
||||
private:
|
||||
using RegisterAccessorMap =
|
||||
std::unordered_map<std::string, std::function<RegisterValueRef(S &)>>;
|
||||
using RegisterConditionList = std::vector<RegisterCondition>;
|
||||
using MemoryConditionList = std::vector<MemoryModifier>;
|
||||
|
||||
remill::Instruction::Category expected_category;
|
||||
RegisterConditionList register_preconditions;
|
||||
RegisterConditionList register_postconditions;
|
||||
MemoryConditionList initial_memory_conditions;
|
||||
MemoryConditionList expected_memory_conditions;
|
||||
RegisterAccessorMap reg_to_accessor;
|
||||
|
||||
template <typename T>
|
||||
T &GetRegister(S &state, const std::string ®_name) const {
|
||||
auto accessor = reg_to_accessor.find(reg_name);
|
||||
if (accessor == reg_to_accessor.end()) {
|
||||
throw std::runtime_error(std::string("Unknown reg: ") + reg_name);
|
||||
}
|
||||
auto wrapper = accessor->second(state);
|
||||
if (auto underlying = std::get_if<std::reference_wrapper<T>>(&wrapper)) {
|
||||
return underlying->get();
|
||||
}
|
||||
throw std::runtime_error(
|
||||
std::string("Reg value " + reg_name + " has incorrect type"));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ApplyCondition(S &state, const std::string ®_name, T value) const {
|
||||
auto ® = this->GetRegister<T>(state, reg_name);
|
||||
reg = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void CheckCondition(S &state, const std::string ®_name, T value) const {
|
||||
auto actual = this->GetRegister<T>(state, reg_name);
|
||||
LOG(INFO) << "Reg: " << reg_name << " Actual: " << std::hex
|
||||
<< static_cast<uint64_t>(actual) << " Expected: " << std::hex
|
||||
<< static_cast<uint64_t>(value);
|
||||
CHECK_EQ(actual, value);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
void AddPrecWrite(uint64_t addr, T value) {
|
||||
this->initial_memory_conditions.push_back(
|
||||
[=](MemoryHandler &mem_hand) { mem_hand.WriteMemory(addr, value); });
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void AddPostRead(uint64_t addr, T value) {
|
||||
this->expected_memory_conditions.push_back([=](MemoryHandler &mem_hand) {
|
||||
LOG(INFO) << "Mem: " << std::hex << addr << " Actual: " << std::hex
|
||||
<< mem_hand.ReadMemory<T>(addr) << " Expected: " << std::hex
|
||||
<< value;
|
||||
CHECK_EQ(mem_hand.ReadMemory<T>(addr), value);
|
||||
});
|
||||
}
|
||||
|
||||
const std::vector<MemoryModifier> &GetMemoryPrecs() const {
|
||||
return this->initial_memory_conditions;
|
||||
}
|
||||
|
||||
const std::vector<MemoryModifier> &GetMemoryPosts() const {
|
||||
return this->expected_memory_conditions;
|
||||
}
|
||||
|
||||
TestOutputSpec(uint64_t disas_addr, std::string target_bytes,
|
||||
remill::Instruction::Category expected_category,
|
||||
RegisterConditionList register_preconditions,
|
||||
RegisterConditionList register_postconditions,
|
||||
RegisterAccessorMap reg_to_accessor)
|
||||
: addr(disas_addr),
|
||||
target_bytes(std::move(target_bytes)),
|
||||
expected_category(expected_category),
|
||||
register_preconditions(std::move(register_preconditions)),
|
||||
register_postconditions(std::move(register_postconditions)),
|
||||
reg_to_accessor(std::move(reg_to_accessor)) {}
|
||||
|
||||
|
||||
void SetupTestPreconditions(S &state) const {
|
||||
for (auto &prec : this->register_preconditions) {
|
||||
std::visit(
|
||||
[&](auto &arg) {
|
||||
this->ApplyCondition(state, prec.register_name, arg);
|
||||
},
|
||||
prec.enforced_value);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckLiftedInstruction(const remill::Instruction &lifted) const {
|
||||
CHECK_EQ(lifted.category, this->expected_category);
|
||||
}
|
||||
|
||||
void CheckResultingState(S &state) const {
|
||||
for (auto &post : this->register_postconditions) {
|
||||
std::visit(
|
||||
[&](auto &arg) {
|
||||
this->CheckCondition(state, post.register_name, arg);
|
||||
},
|
||||
post.enforced_value);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckResultingMemory(MemoryHandler &mem_hand) const {
|
||||
for (const auto &post : this->GetMemoryPosts()) {
|
||||
post(mem_hand);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace test_runner
|
||||
@@ -75,6 +75,23 @@ class MemoryHandler {
|
||||
std::unordered_map<uint64_t, uint8_t> GetUninitializedReads();
|
||||
};
|
||||
|
||||
template <class T>
|
||||
T MemoryHandler::ReadMemory(uint64_t addr) {
|
||||
auto buff = this->readSize(addr, sizeof(T));
|
||||
return llvm::support::endian::read<T>(buff.data(), this->endian);
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
void MemoryHandler::WriteMemory(uint64_t addr, T value) {
|
||||
std::vector<uint8_t> buff(sizeof(T));
|
||||
llvm::support::endian::write<T>(buff.data(), value, this->endian);
|
||||
for (size_t i = 0; i < sizeof(T); i++) {
|
||||
this->state[addr + i] = buff[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void StubOutFlagComputationInstrinsics(llvm::Module *mod,
|
||||
llvm::ExecutionEngine &exec_engine);
|
||||
llvm::Function *
|
||||
@@ -173,11 +190,14 @@ class LiftingTester {
|
||||
remill::ArchName arch_name);
|
||||
std::unordered_map<TypeId, llvm::Type *> GetTypeMapping();
|
||||
|
||||
|
||||
std::optional<std::pair<llvm::Function *, remill::Instruction>>
|
||||
LiftInstructionFunction(std::string_view fname, std::string_view bytes,
|
||||
uint64_t address);
|
||||
|
||||
std::optional<std::pair<llvm::Function *, remill::Instruction>>
|
||||
LiftInstructionFunction(std::string_view fname, std::string_view bytes,
|
||||
uint64_t address, const remill::DecodingContext &ctx);
|
||||
|
||||
const remill::Arch::ArchPtr &GetArch() const;
|
||||
};
|
||||
} // namespace test_runner
|
||||
} // namespace test_runner
|
||||
|
||||
@@ -123,9 +123,6 @@ extern "C" int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
llvm::LLVMContext context;
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
context.enableOpaquePointers();
|
||||
#endif
|
||||
auto os_name = remill::GetOSName(REMILL_OS);
|
||||
auto arch_name = remill::GetArchName(FLAGS_arch);
|
||||
auto arch = remill::Arch::Build(&context, os_name, arch_name);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Copyright (c) 2022 Trail of Bits, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
find_package(GTest CONFIG REQUIRED)
|
||||
list(APPEND PROJECT_LIBRARIES GTest::gtest)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_executable(
|
||||
run-ppc-tests
|
||||
TestLifting.cpp
|
||||
)
|
||||
|
||||
add_test(NAME "ppc-tests" COMMAND "run-ppc-tests")
|
||||
target_link_libraries(
|
||||
run-ppc-tests
|
||||
PRIVATE
|
||||
GTest::gtest
|
||||
remill
|
||||
test-runner
|
||||
glog::glog
|
||||
)
|
||||
|
||||
set_property(TARGET run-ppc-tests PROPERTY ENABLE_EXPORTS ON)
|
||||
set_property(TARGET run-ppc-tests PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
@@ -0,0 +1,774 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Trail of Bits, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <remill/Arch/Arch.h>
|
||||
#include <remill/Arch/Instruction.h>
|
||||
#include <remill/Arch/Name.h>
|
||||
#include <remill/Arch/PPC/Runtime/State.h>
|
||||
#include <remill/BC/Optimizer.h>
|
||||
#include <remill/BC/Version.h>
|
||||
#include <remill/OS/OS.h>
|
||||
#include <test_runner/TestOutputSpec.h>
|
||||
#include <test_runner/TestRunner.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace {
|
||||
|
||||
const static std::unordered_map<
|
||||
std::string, std::function<test_runner::RegisterValueRef(PPCState &)>>
|
||||
reg_to_accessor = {
|
||||
{"pc",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.pc.qword);
|
||||
}},
|
||||
{"r0",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r0.qword);
|
||||
}},
|
||||
{"r1",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r1.qword);
|
||||
}},
|
||||
{"r2",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r2.qword);
|
||||
}},
|
||||
{"r3",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r3.qword);
|
||||
}},
|
||||
{"r4",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r4.qword);
|
||||
}},
|
||||
{"r5",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r5.qword);
|
||||
}},
|
||||
{"r6",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r6.qword);
|
||||
}},
|
||||
{"r7",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r7.qword);
|
||||
}},
|
||||
{"r8",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r8.qword);
|
||||
}},
|
||||
{"r9",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return (st.gpr.r9.qword);
|
||||
}},
|
||||
{"r10",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.gpr.r10.qword;
|
||||
}},
|
||||
{"r11",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.gpr.r11.qword;
|
||||
}},
|
||||
{"r12",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.gpr.r12.qword;
|
||||
}},
|
||||
{"cr",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.iar.cr.qword;
|
||||
}},
|
||||
{"cr0",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.cr_flags.cr0;
|
||||
}},
|
||||
{"cr1",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.cr_flags.cr1;
|
||||
}},
|
||||
{"cr2",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.cr_flags.cr2;
|
||||
}},
|
||||
{"cr3",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.cr_flags.cr3;
|
||||
}},
|
||||
{"cr4",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.cr_flags.cr4;
|
||||
}},
|
||||
{"cr5",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.cr_flags.cr5;
|
||||
}},
|
||||
{"cr6",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.cr_flags.cr6;
|
||||
}},
|
||||
{"cr7",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.cr_flags.cr7;
|
||||
}},
|
||||
{"lr",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.iar.lr.qword;
|
||||
}},
|
||||
{"ctr",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.iar.ctr.qword;
|
||||
}},
|
||||
{"xer",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.iar.xer.qword;
|
||||
}},
|
||||
{"xer_so",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.xer_flags.so;
|
||||
}},
|
||||
{"xer_ov",
|
||||
[](PPCState &st) -> test_runner::RegisterValueRef {
|
||||
return st.xer_flags.ov;
|
||||
}},
|
||||
};
|
||||
|
||||
std::optional<remill::Instruction>
|
||||
GetFlows(std::string_view bytes, uint64_t address, uint64_t vle_val) {
|
||||
|
||||
llvm::LLVMContext context;
|
||||
auto arch = remill::Arch::Build(&context, remill::OSName::kOSLinux,
|
||||
remill::ArchName::kArchPPC);
|
||||
auto sems = remill::LoadArchSemantics(arch.get());
|
||||
|
||||
|
||||
remill::DecodingContext dec_context;
|
||||
dec_context.UpdateContextReg(std::string("VLEReg"), vle_val);
|
||||
CHECK(dec_context.HasValueForReg("VLEReg"));
|
||||
remill::Instruction insn;
|
||||
|
||||
if (!arch->DecodeInstruction(address, bytes, insn, dec_context)) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
return insn;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using test_runner::TestOutputSpec;
|
||||
|
||||
template <test_runner::State S>
|
||||
class TestSpecRunner {
|
||||
private:
|
||||
test_runner::LiftingTester lifter;
|
||||
uint64_t tst_ctr;
|
||||
test_runner::random_bytes_engine rbe;
|
||||
llvm::support::endianness endian;
|
||||
|
||||
public:
|
||||
TestSpecRunner(llvm::LLVMContext &context)
|
||||
: lifter(test_runner::LiftingTester(context, remill::OSName::kOSLinux,
|
||||
remill::kArchPPC)),
|
||||
tst_ctr(0),
|
||||
endian(lifter.GetArch()->MemoryAccessIsLittleEndian()
|
||||
? llvm::support::endianness::little
|
||||
: llvm::support::endianness::big) {}
|
||||
|
||||
void RunTestSpec(const TestOutputSpec<S> &test,
|
||||
const remill::DecodingContext &dec_ctx) {
|
||||
std::stringstream ss;
|
||||
ss << "test_disas_func_" << this->tst_ctr++;
|
||||
|
||||
auto maybe_func = lifter.LiftInstructionFunction(
|
||||
ss.str(), test.target_bytes, test.addr, dec_ctx);
|
||||
|
||||
CHECK(maybe_func.has_value());
|
||||
auto lifted_func = maybe_func->first;
|
||||
|
||||
// Copy lifted function into new module to optimize out intrinsics that aren't used in the lifted function
|
||||
auto new_mod = llvm::CloneModule(*lifted_func->getParent());
|
||||
remill::OptimizeBareModule(new_mod.get());
|
||||
|
||||
auto just_func_mod =
|
||||
std::make_unique<llvm::Module>("", new_mod->getContext());
|
||||
|
||||
auto new_func = test_runner::CopyFunctionIntoNewModule(
|
||||
just_func_mod.get(), lifted_func, new_mod);
|
||||
S st = {};
|
||||
|
||||
test.CheckLiftedInstruction(maybe_func->second);
|
||||
test_runner::RandomizeState(st, this->rbe);
|
||||
|
||||
test.SetupTestPreconditions(st);
|
||||
auto mem_hand = std::make_unique<test_runner::MemoryHandler>(this->endian);
|
||||
|
||||
for (const auto &prec : test.GetMemoryPrecs()) {
|
||||
prec(*mem_hand);
|
||||
}
|
||||
|
||||
test_runner::ExecuteLiftedFunction<S, uint64_t>(
|
||||
new_func, test.target_bytes.length(), &st, mem_hand.get(),
|
||||
[](S *st) { return st->pc.qword; });
|
||||
|
||||
LOG(INFO) << "Pc after execute " << st.pc.qword;
|
||||
test.CheckResultingState(st);
|
||||
|
||||
test.CheckResultingMemory(*mem_hand);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
inline const remill::DecodingContext kVLEContext =
|
||||
remill::DecodingContext({{std::string("VLEReg"), 1}});
|
||||
|
||||
// Add two registers
|
||||
TEST(PPCVLELifts, PPCVLEAdd) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// add r5, r4, r3
|
||||
std::string insn_data("\x7C\xA4\x1A\x14", 4);
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data, remill::Instruction::Category::kCategoryNormal,
|
||||
{{"r4", uint64_t(0xcc)}, {"r3", uint64_t(0xdd)}, {"pc", uint64_t(0x12)}},
|
||||
{{"r5", uint64_t(0x1a9)}, {"pc", uint64_t(0x16)}}, reg_to_accessor);
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// Add two registers and record
|
||||
TEST(PPCVLELifts, PPCVLEAddRecord) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// add. r5, r4, r3
|
||||
// result is positive so cr0[1] is set which is the third bit in little endian
|
||||
std::string insn_data("\x7C\xA4\x1A\x15", 4);
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"r4", uint64_t(0xcc)},
|
||||
{"r3", uint64_t(0xdd)},
|
||||
{"cr0", uint8_t(0)},
|
||||
{"xer_so", uint8_t(0x0)},
|
||||
{"pc", uint64_t(0x12)}},
|
||||
{{"r5", uint64_t(0x1a9)},
|
||||
{"cr0", uint8_t(0b100)},
|
||||
{"xer_so", uint8_t(0x0)},
|
||||
{"pc", uint64_t(0x16)}},
|
||||
reg_to_accessor);
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// Add two registers and set overflow
|
||||
TEST(PPCVLELifts, PPCVLEAddOverflow) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// addo r5, r4, r3
|
||||
std::string insn_data("\x7C\xA4\x1E\x14", 4);
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"r4", uint64_t(5000000000000000000)},
|
||||
{"r3", uint64_t(5000000000000000000)},
|
||||
{"xer_ov", uint8_t(0x0)},
|
||||
{"xer_so", uint8_t(0x0)},
|
||||
{"pc", uint64_t(0x12)}},
|
||||
{{"r5", uint64_t(0x8ac7230489e80000)},
|
||||
{"xer_ov", uint8_t(0x1)},
|
||||
{"xer_so", uint8_t(0x1)},
|
||||
{"pc", uint64_t(0x16)}},
|
||||
reg_to_accessor);
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE short Branch to Link Register
|
||||
TEST(PPCVLELifts, PPCVLEBranchLinkRegister) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// se_blr
|
||||
std::string insn_data("\x00\x04", 2);
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data, remill::Instruction::Category::kCategoryFunctionReturn,
|
||||
{{"lr", uint64_t(0x4)}, {"pc", uint64_t(0x12)}},
|
||||
{{"lr", uint64_t(0x4)}, {"pc", uint64_t(0x4)}}, reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE short Branch to Link Register and Link
|
||||
TEST(PPCVLELifts, PPCVLEBranchLinkRegisterAndLink) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// se_blrl
|
||||
std::string insn_data("\x00\x05", 2);
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data, remill::Instruction::Category::kCategoryFunctionReturn,
|
||||
{{"lr", uint64_t(0x4)}, {"pc", uint64_t(0x12)}},
|
||||
{{"lr", uint64_t(0x14)}, {"pc", uint64_t(0x4)}}, reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE long relative branch that branches to negative relative offset
|
||||
TEST(PPCVLELifts, PPCVLENegBranch) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_b 0xfffffffa (-0x6)
|
||||
std::string insn_data("\x79\xff\xff\xfa", 4);
|
||||
auto maybe_flow = GetFlows(insn_data, 0xdeadbee0, 1);
|
||||
ASSERT_TRUE(maybe_flow.has_value());
|
||||
auto act_insn = *maybe_flow;
|
||||
|
||||
remill::Instruction::InstructionFlowCategory expected_condjmp =
|
||||
remill::Instruction::DirectJump(
|
||||
remill::Instruction::DirectFlow(0xdeadbee0 - 0x6, kVLEContext));
|
||||
|
||||
auto actual_condjmp =
|
||||
std::get<remill::Instruction::DirectJump>(act_insn.flows);
|
||||
|
||||
EXPECT_EQ(expected_condjmp, act_insn.flows);
|
||||
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data, remill::Instruction::Category::kCategoryDirectJump,
|
||||
{{"pc", uint64_t(0x10)}}, {{"pc", uint64_t(0xa)}}, reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE long relative conditional branch
|
||||
TEST(PPCVLELifts, PPCVLECondBranch) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_beq 0xfffffffa (-0x6)
|
||||
std::string insn_data("\x7a\x12\xff\xfa", 4);
|
||||
auto maybe_flow = GetFlows(insn_data, 0xdeadbee0, 1);
|
||||
ASSERT_TRUE(maybe_flow.has_value());
|
||||
auto act_insn = *maybe_flow;
|
||||
|
||||
remill::Instruction::InstructionFlowCategory expected_condjmp =
|
||||
remill::Instruction::ConditionalInstruction(
|
||||
remill::Instruction::DirectJump(
|
||||
remill::Instruction::DirectFlow(0xdeadbee0 - 0x6, kVLEContext)),
|
||||
remill::Instruction::FallthroughFlow(kVLEContext));
|
||||
|
||||
auto actual_condjmp =
|
||||
std::get<remill::Instruction::ConditionalInstruction>(act_insn.flows);
|
||||
|
||||
EXPECT_EQ(expected_condjmp, act_insn.flows);
|
||||
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryConditionalBranch,
|
||||
{{"pc", uint64_t(0x10)}, {"cr0", uint8_t(0b10)}},
|
||||
{{"pc", uint64_t(0xa)}, {"cr0", uint8_t(0b10)}}, reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE long relative conditional branch
|
||||
TEST(PPCVLELifts, PPCVLECondBranch2) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_beq 0xfffffffa (-0x6)
|
||||
std::string insn_data("\x7a\x12\xff\xfa", 4);
|
||||
auto maybe_flow = GetFlows(insn_data, 0xdeadbee0, 1);
|
||||
ASSERT_TRUE(maybe_flow.has_value());
|
||||
auto act_insn = *maybe_flow;
|
||||
|
||||
remill::Instruction::InstructionFlowCategory expected_condjmp =
|
||||
remill::Instruction::ConditionalInstruction(
|
||||
remill::Instruction::DirectJump(
|
||||
remill::Instruction::DirectFlow(0xdeadbee0 - 0x6, kVLEContext)),
|
||||
remill::Instruction::FallthroughFlow(kVLEContext));
|
||||
|
||||
auto actual_condjmp =
|
||||
std::get<remill::Instruction::ConditionalInstruction>(act_insn.flows);
|
||||
|
||||
EXPECT_EQ(expected_condjmp, act_insn.flows);
|
||||
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryConditionalBranch,
|
||||
{{"pc", uint64_t(0x10)}, {"cr0", uint8_t(0b0)}},
|
||||
{{"pc", uint64_t(0x14)}, {"cr0", uint8_t(0b0)}}, reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE long relative branch
|
||||
TEST(PPCVLELifts, PPCVLEBranch) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_b 0x5a
|
||||
std::string insn_data("\x78\x00\x00\x5a", 4);
|
||||
// offset PC by 0x1000012 to also test that relative PC lifting works correctly
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data, remill::Instruction::Category::kCategoryDirectJump,
|
||||
{{"pc", uint64_t(0x1000012)}}, {{"pc", uint64_t(0x1000012 + 0x5a)}},
|
||||
reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE short compare immediate
|
||||
TEST(PPCVLELifts, PPCVLECompareImmediate) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// se_cmpi r7, 0x0
|
||||
std::string insn_data("\x2a\x07", 2);
|
||||
// cr1[2], set when result is zero
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)},
|
||||
{"r7", uint64_t(0x0)},
|
||||
{"xer_so", uint8_t(0)},
|
||||
{"cr0", uint8_t(0)}},
|
||||
{{"pc", uint64_t(0x12 + 2)},
|
||||
{"xer_so", uint8_t(0)},
|
||||
{"cr0", uint8_t(0b10)}},
|
||||
reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE Store word
|
||||
TEST(PPCVLELifts, PPCVLEStoreWord) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_stw r5, 0x10(r4)
|
||||
std::string insn_data("\x54\xa4\x00\x10", 4);
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)},
|
||||
{"r5", uint64_t(0x13371337)},
|
||||
{"r4", uint64_t(0xdeadbee0)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r5", uint64_t(0x13371337)},
|
||||
{"r4", uint64_t(0xdeadbee0)}},
|
||||
reg_to_accessor);
|
||||
spec.AddPrecWrite<uint32_t>(0xdeadbee0 + 0x10, 0x0);
|
||||
spec.AddPostRead<uint32_t>(0xdeadbee0 + 0x10, 0x13371337);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// Load word and zero
|
||||
TEST(PPCVLELifts, PPCVLELoadWordAndZero) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_lwz r5, 0x10(r4)
|
||||
std::string insn_data("\x50\xa4\x00\x10", 4);
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)},
|
||||
{"r5", uint64_t(0x0)},
|
||||
{"r4", uint64_t(0xdeadbee0)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r5", uint64_t(0x13371337)},
|
||||
{"r4", uint64_t(0xdeadbee0)}},
|
||||
reg_to_accessor);
|
||||
spec.AddPrecWrite<uint32_t>(0xdeadbee0 + 0x10, 0x13371337);
|
||||
spec.AddPostRead<uint32_t>(0xdeadbee0 + 0x10, 0x13371337);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE Load Multiple Volatile General Purpose Registers
|
||||
// Instruction only operates on the 32bit register sizes
|
||||
TEST(PPCVLELifts, PPCVLELoadMultipleGeneralPurposeRegisters) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_ldmvgprw 0x0(r1)
|
||||
std::string insn_data("\x18\x01\x10\x00", 4);
|
||||
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)},
|
||||
{"r1", uint64_t(0x13370)},
|
||||
{"r0", uint64_t(0x0)},
|
||||
{"r3", uint64_t(0x0)},
|
||||
{"r4", uint64_t(0x0)},
|
||||
{"r5", uint64_t(0x0)},
|
||||
{"r6", uint64_t(0x0)},
|
||||
{"r7", uint64_t(0x0)},
|
||||
{"r8", uint64_t(0x0)},
|
||||
{"r9", uint64_t(0x0)},
|
||||
{"r10", uint64_t(0x0)},
|
||||
{"r11", uint64_t(0x0)},
|
||||
{"r12", uint64_t(0x0)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r1", uint64_t(0x13370)},
|
||||
{"r0", uint64_t(0x11223344)},
|
||||
{"r3", uint64_t(0x22114433)},
|
||||
{"r4", uint64_t(0x99aabbcc)},
|
||||
{"r5", uint64_t(0xaa99ccbb)},
|
||||
{"r6", uint64_t(0x88776655)},
|
||||
{"r7", uint64_t(0x77885566)},
|
||||
{"r8", uint64_t(0x00ffeedd)},
|
||||
{"r9", uint64_t(0xff00ddee)},
|
||||
{"r10", uint64_t(0x44332211)},
|
||||
{"r11", uint64_t(0xccbbaa99)},
|
||||
{"r12", uint64_t(0xbbcc99aa)}},
|
||||
reg_to_accessor);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370, 0x11223344);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x4, 0x22114433);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x8, 0x99aabbcc);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0xc, 0xaa99ccbb);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x10, 0x88776655);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x14, 0x77885566);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x18, 0x00ffeedd);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x1c, 0xff00ddee);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x20, 0x44332211);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x24, 0xccbbaa99);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x28, 0xbbcc99aa);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE Store Multiple Volatile General Purpose Registers
|
||||
TEST(PPCVLELifts, PPCVLEStoreMultipleGeneralPurposeRegisters) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_stmvgprw 0x0(r1)
|
||||
std::string insn_data("\x18\x01\x11\x00", 4);
|
||||
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)},
|
||||
{"r1", uint64_t(0x13370)},
|
||||
{"r0", uint64_t(0x11223344)},
|
||||
{"r3", uint64_t(0x22114433)},
|
||||
{"r4", uint64_t(0x99aabbcc)},
|
||||
{"r5", uint64_t(0xaa99ccbb)},
|
||||
{"r6", uint64_t(0x88776655)},
|
||||
{"r7", uint64_t(0x77885566)},
|
||||
{"r8", uint64_t(0x00ffeedd)},
|
||||
{"r9", uint64_t(0xff00ddee)},
|
||||
{"r10", uint64_t(0x44332211)},
|
||||
{"r11", uint64_t(0xccbbaa99)},
|
||||
{"r12", uint64_t(0xbbcc99aa)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r1", uint64_t(0x13370)},
|
||||
{"r0", uint64_t(0x11223344)},
|
||||
{"r3", uint64_t(0x22114433)},
|
||||
{"r4", uint64_t(0x99aabbcc)},
|
||||
{"r5", uint64_t(0xaa99ccbb)},
|
||||
{"r6", uint64_t(0x88776655)},
|
||||
{"r7", uint64_t(0x77885566)},
|
||||
{"r8", uint64_t(0x00ffeedd)},
|
||||
{"r9", uint64_t(0xff00ddee)},
|
||||
{"r10", uint64_t(0x44332211)},
|
||||
{"r11", uint64_t(0xccbbaa99)},
|
||||
{"r12", uint64_t(0xbbcc99aa)}},
|
||||
reg_to_accessor);
|
||||
spec.AddPostRead<uint32_t>(0x13370, 0x11223344);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x4, 0x22114433);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x8, 0x99aabbcc);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0xc, 0xaa99ccbb);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x10, 0x88776655);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x14, 0x77885566);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x18, 0x00ffeedd);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x1c, 0xff00ddee);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x20, 0x44332211);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x24, 0xccbbaa99);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x28, 0xbbcc99aa);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE Load Multiple Volatile Special Purpose Registers
|
||||
TEST(PPCVLELifts, PPCVLELoadMultipleSpecialPurposeRegisters) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_ldmvsprw 0x0(r1)
|
||||
std::string insn_data("\x18\x21\x10\x00", 4);
|
||||
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)},
|
||||
{"r1", uint64_t(0x13370)},
|
||||
{"cr", uint64_t(0x0)},
|
||||
{"cr0", uint8_t(0x0)},
|
||||
{"cr1", uint8_t(0x0)},
|
||||
{"cr2", uint8_t(0x0)},
|
||||
{"cr3", uint8_t(0x0)},
|
||||
{"cr4", uint8_t(0x0)},
|
||||
{"cr5", uint8_t(0x0)},
|
||||
{"cr6", uint8_t(0x0)},
|
||||
{"cr7", uint8_t(0x0)},
|
||||
{"lr", uint64_t(0x0)},
|
||||
{"ctr", uint64_t(0x0)},
|
||||
{"xer", uint64_t(0x0)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r1", uint64_t(0x13370)},
|
||||
// each crN register is 4-bits
|
||||
{"cr0", uint8_t(0x1)},
|
||||
{"cr1", uint8_t(0x2)},
|
||||
{"cr2", uint8_t(0x3)},
|
||||
{"cr3", uint8_t(0x4)},
|
||||
{"cr4", uint8_t(0x5)},
|
||||
{"cr5", uint8_t(0x6)},
|
||||
{"cr6", uint8_t(0x7)},
|
||||
{"cr7", uint8_t(0x8)},
|
||||
{"lr", uint64_t(0x55667788)},
|
||||
{"ctr", uint64_t(0x99aabbcc)},
|
||||
{"xer", uint64_t(0xddeeff00)}},
|
||||
reg_to_accessor);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370, 0x87654321);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x4, 0x55667788);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0x8, 0x99aabbcc);
|
||||
spec.AddPrecWrite<uint32_t>(0x13370 + 0xc, 0xddeeff00);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// VLE Store Multiple Volatile Special Purpose Registers
|
||||
// TODO(wtan): Disabled for now due to bug in Ghidra pcode for this instruction
|
||||
TEST(PPCVLELifts, DISABLED_PPCVLEStoreMultipleSpecialPurposeRegisters) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_stmvsprw 0x0(r1)
|
||||
std::string insn_data("\x18\x21\x11\x00", 4);
|
||||
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)},
|
||||
{"r1", uint64_t(0x13370)},
|
||||
{"cr", uint64_t(0x11223344)},
|
||||
{"lr", uint64_t(0x55667788)},
|
||||
{"ctr", uint64_t(0x99aabbcc)},
|
||||
{"cr0", uint8_t(0x1)},
|
||||
{"cr1", uint8_t(0x2)},
|
||||
{"cr2", uint8_t(0x3)},
|
||||
{"cr3", uint8_t(0x4)},
|
||||
{"cr4", uint8_t(0x5)},
|
||||
{"cr5", uint8_t(0x6)},
|
||||
{"cr6", uint8_t(0x7)},
|
||||
{"cr7", uint8_t(0x8)},
|
||||
{"xer", uint64_t(0xddeeff00)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r1", uint64_t(0x13370)},
|
||||
{"cr0", uint8_t(0x1)},
|
||||
{"cr1", uint8_t(0x2)},
|
||||
{"cr2", uint8_t(0x3)},
|
||||
{"cr3", uint8_t(0x4)},
|
||||
{"cr4", uint8_t(0x5)},
|
||||
{"cr5", uint8_t(0x6)},
|
||||
{"cr6", uint8_t(0x7)},
|
||||
{"cr7", uint8_t(0x8)},
|
||||
{"lr", uint64_t(0x55667788)},
|
||||
{"ctr", uint64_t(0x99aabbcc)},
|
||||
{"xer", uint64_t(0xddeeff00)}},
|
||||
reg_to_accessor);
|
||||
spec.AddPostRead<uint32_t>(0x13370, 0x87654321);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x4, 0x55667788);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0x8, 0x99aabbcc);
|
||||
spec.AddPostRead<uint32_t>(0x13370 + 0xc, 0xddeeff00);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// Rotate Left Word Immediate then AND with Mask
|
||||
// Tests internal conditional branches in pcode
|
||||
TEST(PPCVLELifts, PPCVLERotateLeftWordImmediateAndMask) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_rlwinm r6, r5, 0x1e, 0x1d, 0x1f
|
||||
// n >> 2 & 7
|
||||
// (n & 31) >> 2
|
||||
std::string insn_data("\x74\xa6\xf7\x7f", 4);
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data, remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)}, {"r5", uint64_t(0x1337)}, {"r6", uint64_t(0x0)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r5", uint64_t(0x1337)},
|
||||
{"r6", uint64_t(0x5)}},
|
||||
reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// Convert Floating-Point Double-Precision from Signed Integer
|
||||
TEST(PPCVLELifts, PPCVLEConvertDoubleFromSignedInteger) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// efdcfsi r5, r4
|
||||
std::string insn_data("\x10\xa0\x22\xf1", 4);
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data, remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)}, {"r4", uint64_t(0x1337)}, {"r5", uint64_t(0x0)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r4", uint64_t(0x1337)},
|
||||
{"r5", uint64_t(0x40b3370000000000)}},
|
||||
reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// Convert Floating-Point Single-Precision from Signed Integer
|
||||
TEST(PPCVLELifts, PPCVLEConvertFloatFromSignedInteger) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// efscfsi r5, r4
|
||||
std::string insn_data("\x10\xa0\x22\xd1", 4);
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data, remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)}, {"r4", uint64_t(0x1337)}, {"r5", uint64_t(0x0)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r4", uint64_t(0x1337)},
|
||||
{"r5", uint64_t(0x4599b800)}},
|
||||
reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// Convert Floating-Point Single-Precision to Signed Integer
|
||||
TEST(PPCVLELifts, PPCVLEConvertFloatToSignedInteger) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// efsctsi r5, r4
|
||||
std::string insn_data("\x10\xa0\x22\xd5", 4);
|
||||
TestOutputSpec<PPCState> spec(0x12, insn_data,
|
||||
remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)},
|
||||
{"r4", uint64_t(0x4599b800)},
|
||||
{"r5", uint64_t(0x0)}},
|
||||
{{"pc", uint64_t(0x12 + 4)},
|
||||
{"r4", uint64_t(0x4599b800)},
|
||||
{"r5", uint64_t(0x1337)}},
|
||||
reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
|
||||
// Test syscall
|
||||
// TODO(wtan): Disabled, callother not supported
|
||||
TEST(PPCVLELifts, DISABLED_PPCVLESyscall) {
|
||||
llvm::LLVMContext curr_context;
|
||||
// e_sc
|
||||
std::string insn_data("\x7c\x00\x00\x48", 4);
|
||||
TestOutputSpec<PPCState> spec(
|
||||
0x12, insn_data, remill::Instruction::Category::kCategoryNormal,
|
||||
{{"pc", uint64_t(0x12)}}, {{"pc", uint64_t(0x12 + 4)}}, reg_to_accessor);
|
||||
|
||||
TestSpecRunner<PPCState> runner(curr_context);
|
||||
runner.RunTestSpec(spec, kVLEContext);
|
||||
}
|
||||
@@ -34,6 +34,3 @@ target_link_libraries(
|
||||
|
||||
set_property(TARGET run-thumb-tests PROPERTY ENABLE_EXPORTS ON)
|
||||
set_property(TARGET run-thumb-tests PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
message(STATUS "Adding test: thumb as run-thumb-tests")
|
||||
add_test(NAME "thumb" COMMAND "run-thumb-tests")
|
||||
|
||||
@@ -60,9 +60,6 @@ std::optional<remill::Instruction> GetFlows(std::string_view bytes,
|
||||
uint64_t address, uint64_t tm_val) {
|
||||
|
||||
llvm::LLVMContext context;
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
context.enableOpaquePointers();
|
||||
#endif
|
||||
auto arch = remill::Arch::Build(&context, remill::OSName::kOSLinux,
|
||||
remill::ArchName::kArchAArch32LittleEndian);
|
||||
auto sems = remill::LoadArchSemantics(arch.get());
|
||||
@@ -188,11 +185,11 @@ class TestSpecRunner {
|
||||
auto new_mod = llvm::CloneModule(*lifted_func->getParent());
|
||||
remill::OptimizeBareModule(new_mod.get());
|
||||
|
||||
auto justFuncMod =
|
||||
auto just_func_mod =
|
||||
std::make_unique<llvm::Module>("", new_mod->getContext());
|
||||
|
||||
auto new_func = test_runner::CopyFunctionIntoNewModule(
|
||||
justFuncMod.get(), lifted_func, new_mod);
|
||||
just_func_mod.get(), lifted_func, new_mod);
|
||||
AArch32State st = {};
|
||||
|
||||
|
||||
@@ -240,9 +237,6 @@ TEST(ThumbRandomizedLifts, PopPC) {
|
||||
spec.AddPrecWrite<uint32_t>(10, 16);
|
||||
llvm::LLVMContext context;
|
||||
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
context.enableOpaquePointers();
|
||||
#endif
|
||||
TestSpecRunner runner(context);
|
||||
runner.RunTestSpec(spec);
|
||||
}
|
||||
@@ -259,18 +253,12 @@ TEST(ThumbRandomizedLifts, RelPcTest) {
|
||||
spec.AddPrecWrite<uint32_t>(32, 0xdeadc0de);
|
||||
llvm::LLVMContext context;
|
||||
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
context.enableOpaquePointers();
|
||||
#endif
|
||||
TestSpecRunner runner(context);
|
||||
runner.RunTestSpec(spec);
|
||||
}
|
||||
|
||||
TEST(RegressionTests, AARCH64RegSize) {
|
||||
llvm::LLVMContext context;
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
context.enableOpaquePointers();
|
||||
#endif
|
||||
auto arch = remill::Arch::Build(&context, remill::OSName::kOSLinux,
|
||||
remill::ArchName::kArchAArch64LittleEndian);
|
||||
auto sems = remill::LoadArchSemantics(arch.get());
|
||||
@@ -291,9 +279,6 @@ TEST(RegressionTests, AARCH64RegSize) {
|
||||
}
|
||||
TEST(RegressionTests, Armv8FPSCR) {
|
||||
llvm::LLVMContext context;
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
context.enableOpaquePointers();
|
||||
#endif
|
||||
auto arch = remill::Arch::Build(&context, remill::OSName::kOSLinux,
|
||||
remill::ArchName::kArchAArch32LittleEndian);
|
||||
CHECK_NOTNULL(arch->RegisterByName("FPSCR"));
|
||||
|
||||
@@ -124,9 +124,6 @@ extern "C" int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
llvm::LLVMContext context;
|
||||
#if LLVM_VERSION_NUMBER < LLVM_VERSION(15, 0)
|
||||
context.enableOpaquePointers();
|
||||
#endif
|
||||
auto os_name = remill::GetOSName(REMILL_OS);
|
||||
auto arch_name = remill::GetArchName(FLAGS_arch);
|
||||
auto arch = remill::Arch::Build(&context, os_name, arch_name);
|
||||
|
||||
Reference in New Issue
Block a user