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:
Alex Cameron
2023-02-03 02:05:28 +11:00
committed by GitHub
parent 9005cb0fc8
commit fb018c96e9
62 changed files with 3146 additions and 298 deletions
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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}
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+1 -1
View File
@@ -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)
+7 -2
View File
@@ -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);
+7 -3
View File
@@ -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
+17
View File
@@ -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);
+387
View File
@@ -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
+2
View File
@@ -93,6 +93,8 @@ class SyncHyperCall {
kSPARCTrapCondNEG,
kSPARCTrapCondVC,
kSPARCTrapCondVS,
kPPCEmulateInstruction,
};
} __attribute__((packed));
+2 -1
View File
@@ -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__)
+3
View File
@@ -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
+87
View File
@@ -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
+16 -6
View File
@@ -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
+5
View File
@@ -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);
+8 -17
View File
@@ -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
View File
@@ -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;
}
+1
View File
@@ -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)
+7 -3
View File
@@ -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
+1
View File
@@ -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;
}
};
+5
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
add_subdirectory(Runtime)
+44
View File
@@ -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"
)
+43
View File
@@ -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;
+9
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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
)
+73 -22
View File
@@ -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
+7 -4
View File
@@ -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
+40
View File
@@ -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
+269
View File
@@ -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
+4 -3
View File
@@ -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
+2 -1
View File
@@ -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);
}
+3 -2
View File
@@ -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 {
+1
View File
@@ -31,6 +31,7 @@ add_library(remill_bc STATIC
Optimizer.cpp
TraceLifter.cpp
SleighLifter.cpp
PcodeCFG.cpp
Util.cpp
)
+163
View File
@@ -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
View File
@@ -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);
}
+28
View File
@@ -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",
+59
View File
@@ -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)
+196
View File
@@ -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)
+97
View File
@@ -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)
+120
View File
@@ -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)
+40
View File
@@ -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
View File
@@ -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
-3
View File
@@ -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__":
+1
View File
@@ -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(
+12 -21
View File
@@ -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 &reg_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 &reg_name, T value) const {
auto &reg = this->GetRegister<T>(state, reg_name);
reg = value;
}
template <typename T>
void CheckCondition(S &state, const std::string &reg_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
-3
View File
@@ -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);
+36
View File
@@ -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)
+774
View File
@@ -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);
}
-3
View File
@@ -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")
+2 -17
View File
@@ -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"));
-3
View File
@@ -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);