Files
lifting-bits-remill/lib/BC/PcodeCFG.cpp
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

164 lines
4.3 KiB
C++

#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