Files
lifting-bits-remill/test_runner_lib/include/test_runner/TestOutputSpec.h
T
Alex Cameron fb018c96e9 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>
2023-02-02 10:05:28 -05:00

161 lines
5.1 KiB
C++

/*
* 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