mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
fb018c96e9
* 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>
775 lines
30 KiB
C++
775 lines
30 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.
|
|
*/
|
|
|
|
#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);
|
|
}
|