| // Copyright 2012 Google Inc. All Rights Reserved. |
| // |
| // 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 "syzygy/block_graph/basic_block_assembler.h" |
| |
| #include "gtest/gtest.h" |
| #include "syzygy/block_graph/basic_block_subgraph.h" |
| |
| namespace block_graph { |
| |
| namespace { |
| |
| class BasicBlockAssemblerTest : public testing::Test { |
| public: |
| typedef BlockGraph::RelativeAddress RelativeAddress; |
| typedef BlockGraph::Block::SourceRange SourceRange; |
| |
| BasicBlockAssemblerTest(); |
| |
| void SetUp() override {} |
| |
| void TearDown() override {} |
| |
| protected: |
| struct Ref { |
| size_t offset; |
| BasicBlockReference::ReferredType type; |
| const void* reference; |
| }; |
| |
| template <size_t N> |
| void AssertRefs(const Ref(& refs)[N]) { |
| ASSERT_EQ(1, instructions_.size()); |
| const Instruction& instr = instructions_.front(); |
| |
| for (size_t i = 0; i < N; ++i) { |
| BasicBlock::BasicBlockReferenceMap::const_iterator it = |
| instr.references().find(refs[i].offset); |
| ASSERT_NE(instr.references().end(), it); |
| ASSERT_EQ(refs[i].type, it->second.referred_type()); |
| switch (refs[i].type) { |
| case BasicBlockReference::REFERRED_TYPE_BLOCK: |
| ASSERT_EQ(refs[i].reference, it->second.block()); |
| break; |
| case BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK: |
| ASSERT_EQ(refs[i].reference, it->second.basic_block()); |
| break; |
| default: |
| ASSERT_TRUE(false); |
| } |
| |
| ASSERT_EQ(refs[i].type, it->second.referred_type()); |
| } |
| |
| instructions_.clear(); |
| } |
| |
| void AssertNoRefs() { |
| ASSERT_EQ(1, instructions_.size()); |
| ASSERT_EQ(0, instructions_.front().references().size()); |
| instructions_.clear(); |
| } |
| |
| BlockGraph block_graph_; |
| BlockGraph::Block* test_block_; |
| BasicBlockSubGraph subgraph_; |
| BasicCodeBlock* test_bb_; |
| BasicBlock::Instructions instructions_; |
| BasicBlockAssembler asm_; |
| }; |
| |
| #define ASSERT_REFS(...) \ |
| do { \ |
| const Ref refs[] = { __VA_ARGS__ }; \ |
| ASSERT_NO_FATAL_FAILURE(AssertRefs(refs)); \ |
| } while (0) |
| |
| #define ASSERT_NO_REFS() ASSERT_NO_FATAL_FAILURE(AssertNoRefs()) |
| |
| BasicBlockAssemblerTest::BasicBlockAssemblerTest() |
| : test_block_(NULL), |
| test_bb_(NULL), |
| asm_(instructions_.end(), &instructions_) { |
| test_block_ = block_graph_.AddBlock(BlockGraph::CODE_BLOCK, 10, "test block"); |
| test_bb_ = subgraph_.AddBasicCodeBlock("foo"); |
| } |
| |
| } // namespace |
| |
| TEST(UntypedReferenceTest, DefaultConstructor) { |
| UntypedReference r; |
| EXPECT_EQ(NULL, r.basic_block()); |
| EXPECT_EQ(NULL, r.block()); |
| EXPECT_EQ(0, r.offset()); |
| EXPECT_EQ(0, r.base()); |
| EXPECT_FALSE(r.IsValid()); |
| EXPECT_EQ(BasicBlockReference::REFERRED_TYPE_UNKNOWN, r.referred_type()); |
| } |
| |
| TEST(UntypedReferenceTest, BasicBlockReferenceToBasicBlockConstructor) { |
| BasicBlockSubGraph subgraph; |
| BasicCodeBlock* bcb = subgraph.AddBasicCodeBlock("foo"); |
| BasicBlock* bb = bcb; |
| BasicBlockReference bbref(BlockGraph::ABSOLUTE_REF, 4, bcb); |
| UntypedReference r(bbref); |
| EXPECT_EQ(bb, r.basic_block()); |
| EXPECT_EQ(NULL, r.block()); |
| EXPECT_EQ(0, r.offset()); |
| EXPECT_EQ(0, r.base()); |
| EXPECT_TRUE(r.IsValid()); |
| EXPECT_EQ(BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, r.referred_type()); |
| } |
| |
| TEST(UntypedReferenceTest, BasicBlockReferenceToBlockConstructor) { |
| BlockGraph block_graph; |
| BlockGraph::Block* b = |
| block_graph.AddBlock(BlockGraph::CODE_BLOCK, 20, "foo"); |
| BasicBlockReference bbref(BlockGraph::ABSOLUTE_REF, 4, b, 4, 10); |
| UntypedReference r(bbref); |
| EXPECT_EQ(NULL, r.basic_block()); |
| EXPECT_EQ(b, r.block()); |
| EXPECT_EQ(4, r.offset()); |
| EXPECT_EQ(10, r.base()); |
| EXPECT_TRUE(r.IsValid()); |
| EXPECT_EQ(BasicBlockReference::REFERRED_TYPE_BLOCK, r.referred_type()); |
| } |
| |
| TEST(UntypedReferenceTest, BasicBlockConstructor) { |
| BasicBlockSubGraph subgraph; |
| BasicCodeBlock* bcb = subgraph.AddBasicCodeBlock("foo"); |
| BasicBlock* bb = bcb; |
| UntypedReference r(bcb); |
| EXPECT_EQ(bb, r.basic_block()); |
| EXPECT_EQ(NULL, r.block()); |
| EXPECT_EQ(0, r.offset()); |
| EXPECT_EQ(0, r.base()); |
| EXPECT_TRUE(r.IsValid()); |
| EXPECT_EQ(BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, r.referred_type()); |
| } |
| |
| TEST(UntypedReferenceTest, BlockConstructor) { |
| BlockGraph block_graph; |
| BlockGraph::Block* b = |
| block_graph.AddBlock(BlockGraph::CODE_BLOCK, 0, "dummy"); |
| UntypedReference r(b, 4, 10); |
| EXPECT_EQ(NULL, r.basic_block()); |
| EXPECT_EQ(b, r.block()); |
| EXPECT_EQ(4, r.offset()); |
| EXPECT_EQ(10, r.base()); |
| EXPECT_TRUE(r.IsValid()); |
| EXPECT_EQ(BasicBlockReference::REFERRED_TYPE_BLOCK, r.referred_type()); |
| } |
| |
| TEST(UntypedReferenceTest, CopyConstructor) { |
| BlockGraph block_graph; |
| BlockGraph::Block* b = |
| block_graph.AddBlock(BlockGraph::CODE_BLOCK, 0, "dummy"); |
| UntypedReference r1(b, 4, 10); |
| |
| UntypedReference r2(r1); |
| EXPECT_EQ(r1.basic_block(), r2.basic_block()); |
| EXPECT_EQ(r1.block(), r2.block()); |
| EXPECT_EQ(r1.offset(), r2.offset()); |
| EXPECT_EQ(r1.base(), r2.base()); |
| EXPECT_EQ(r1.IsValid(), r2.IsValid()); |
| } |
| |
| TEST(UntypedReferenceTest, Comparison) { |
| BlockGraph block_graph; |
| BasicBlockSubGraph subgraph; |
| BlockGraph::Block* b = |
| block_graph.AddBlock(BlockGraph::CODE_BLOCK, 0, "dummy"); |
| UntypedReference r1(b, 4, 10); |
| |
| UntypedReference r2(b, 0, 0); |
| EXPECT_FALSE(r1 == r2); |
| |
| BasicCodeBlock* bcb = subgraph.AddBasicCodeBlock("foo"); |
| UntypedReference r3(bcb); |
| EXPECT_FALSE(r1 == r3); |
| EXPECT_FALSE(r2 == r3); |
| |
| UntypedReference r4(r1); |
| EXPECT_TRUE(r1 == r4); |
| |
| UntypedReference r5(r2); |
| EXPECT_TRUE(r2 == r5); |
| } |
| |
| namespace { |
| |
| template <typename ValueTraits> |
| class ValueTest : public BasicBlockAssemblerTest { |
| public: |
| typedef typename ValueTraits ValueTraits; |
| typedef typename ValueTraits::ValueType ValueType; |
| |
| void TestValue(const ValueType& value, |
| uint32_t expected_value, |
| assm::ValueSize expected_size) { |
| EXPECT_EQ(expected_size, value.size()); |
| EXPECT_EQ(expected_value, value.value()); |
| EXPECT_EQ(BasicBlockReference::REFERRED_TYPE_UNKNOWN, |
| value.reference().referred_type()); |
| |
| auto value_copy(value); |
| EXPECT_EQ(expected_size, value_copy.size()); |
| EXPECT_EQ(expected_value, value_copy.value()); |
| EXPECT_EQ(BasicBlockReference::REFERRED_TYPE_UNKNOWN, |
| value_copy.reference().referred_type()); |
| |
| EXPECT_TRUE(value == value_copy); |
| |
| auto value_diff(ValueTraits::Factory(expected_value - 1)); |
| EXPECT_FALSE(value == value_diff); |
| } |
| |
| void Test8BitValue(uint32_t input_value, uint32_t expected_value) { |
| TestValue(ValueTraits::Factory(input_value), |
| expected_value, assm::kSize8Bit); |
| TestValue(ValueTraits::Factory(input_value, assm::kSize8Bit), |
| expected_value, assm::kSize8Bit); |
| } |
| |
| void Test32BitValue(uint32_t input_value, uint32_t expected_value) { |
| TestValue(ValueTraits::Factory(input_value), |
| expected_value, assm::kSize32Bit); |
| TestValue(ValueTraits::Factory(input_value, assm::kSize32Bit), |
| expected_value, assm::kSize32Bit); |
| } |
| }; |
| |
| struct ImmediateTestTraits { |
| typedef BasicBlockAssembler::Immediate ValueType; |
| |
| static ValueType Factory() { return Immediate(); } |
| static ValueType Factory(uint32_t value) { return Immediate(value); } |
| static ValueType Factory(uint32_t value, assm::ValueSize size) { |
| return Immediate(value, size); |
| } |
| static ValueType Factory(BasicBlock* bb) { return Immediate(bb); } |
| static ValueType Factory(BlockGraph::Block* block, |
| BlockGraph::Offset offset) { |
| return Immediate(block, offset); |
| } |
| static ValueType Factory(BlockGraph::Block* block, |
| BlockGraph::Offset offset, |
| BlockGraph::Offset base) { |
| return Immediate(block, offset, base); |
| } |
| static ValueType Factory(uint32_t value, |
| ValueSize size, |
| const UntypedReference& ref) { |
| return Immediate(value, size, ref); |
| } |
| }; |
| |
| struct DisplacementTestTraits { |
| typedef BasicBlockAssembler::Displacement ValueType; |
| |
| static ValueType Factory() { return Displacement(); } |
| static ValueType Factory(uint32_t value) { return Displacement(value); } |
| static ValueType Factory(uint32_t value, assm::ValueSize size) { |
| return Displacement(value, size); |
| } |
| static ValueType Factory(BasicBlock* bb) { return Displacement(bb); } |
| static ValueType Factory(BlockGraph::Block* block, |
| BlockGraph::Offset offset) { |
| return Displacement(block, offset); |
| } |
| static ValueType Factory(BlockGraph::Block* block, |
| BlockGraph::Offset offset, |
| BlockGraph::Offset base) { |
| return Displacement(block, offset, base); |
| } |
| static ValueType Factory(uint32_t value, |
| ValueSize size, |
| const UntypedReference& ref) { |
| return Displacement(value, size, ref); |
| } |
| }; |
| |
| } // namespace |
| |
| typedef ::testing::Types<ImmediateTestTraits, DisplacementTestTraits> |
| ValueTestTypes; |
| TYPED_TEST_CASE(ValueTest, ValueTestTypes); |
| |
| TYPED_TEST(ValueTest, Factories) { |
| { |
| auto imm_empty(ValueTraits::Factory()); |
| ASSERT_EQ(0, imm_empty.value()); |
| ASSERT_EQ(assm::kSizeNone, imm_empty.size()); |
| ASSERT_EQ(BasicBlockReference::REFERRED_TYPE_UNKNOWN, |
| imm_empty.reference().referred_type()); |
| } |
| |
| Test8BitValue(0, 0); |
| Test8BitValue(127, 127); |
| |
| Test8BitValue(static_cast<uint32_t>(-128), 0xFFFFFF80); |
| Test8BitValue(0, 0); |
| Test8BitValue(127, 0x0000007F); |
| |
| Test32BitValue(128, 0x00000080); |
| Test32BitValue(0xCAFEBABE, 0xCAFEBABE); |
| |
| Test32BitValue(static_cast<uint32_t>(-129), 0xFFFFFF7F); |
| Test32BitValue(128, 0x000000080); |
| Test32BitValue(0xBABE, 0xBABE); |
| |
| { |
| const BlockGraph::Offset kOffs = 10; |
| auto imm_block_ref(ValueTraits::Factory(test_block_, kOffs)); |
| |
| ASSERT_EQ(0, imm_block_ref.value()); |
| ASSERT_EQ(assm::kSize32Bit, imm_block_ref.size()); |
| ASSERT_EQ(BasicBlockReference::REFERRED_TYPE_BLOCK, |
| imm_block_ref.reference().referred_type()); |
| ASSERT_EQ(test_block_, imm_block_ref.reference().block()); |
| ASSERT_EQ(kOffs, imm_block_ref.reference().offset()); |
| ASSERT_EQ(kOffs, imm_block_ref.reference().base()); |
| } |
| |
| { |
| auto imm_bb_ref(ValueTraits::Factory(test_bb_)); |
| |
| ASSERT_EQ(0, imm_bb_ref.value()); |
| ASSERT_EQ(assm::kSize32Bit, imm_bb_ref.size()); |
| ASSERT_EQ(BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, |
| imm_bb_ref.reference().referred_type()); |
| ASSERT_EQ(test_bb_, imm_bb_ref.reference().basic_block()); |
| ASSERT_EQ(0, imm_bb_ref.reference().offset()); |
| ASSERT_EQ(0, imm_bb_ref.reference().base()); |
| } |
| |
| { |
| // Explicitly specified size and reference info. |
| UntypedReference ref(test_block_, 1, 2); |
| auto imm_expl_ref(ValueTraits::Factory(0xBE, assm::kSize8Bit, ref)); |
| |
| ASSERT_EQ(0xBE, imm_expl_ref.value()); |
| ASSERT_EQ(assm::kSize8Bit, imm_expl_ref.size()); |
| ASSERT_EQ(BasicBlockReference::REFERRED_TYPE_BLOCK, |
| imm_expl_ref.reference().referred_type()); |
| ASSERT_EQ(test_block_, imm_expl_ref.reference().block()); |
| ASSERT_EQ(1, imm_expl_ref.reference().offset()); |
| ASSERT_EQ(2, imm_expl_ref.reference().base()); |
| } |
| } |
| |
| namespace { |
| |
| // Asserts that @p op.displacement() is equal to @p displ. |
| void TestEqualDisplacement(const BasicBlockAssembler::Operand& op, |
| const BasicBlockAssembler::Displacement& displ) { |
| ASSERT_EQ(displ.value(), op.displacement().value()); |
| ASSERT_EQ(displ.size(), op.displacement().size()); |
| |
| ASSERT_EQ(displ.reference().IsValid(), |
| op.displacement().reference().IsValid()); |
| if (!displ.reference().IsValid()) |
| return; |
| |
| ASSERT_EQ(displ.reference().IsValid(), |
| op.displacement().reference().IsValid()); |
| |
| ASSERT_EQ(displ.reference().basic_block(), |
| op.displacement().reference().basic_block()); |
| ASSERT_EQ(displ.reference().block(), |
| op.displacement().reference().block()); |
| ASSERT_EQ(displ.reference().offset(), |
| op.displacement().reference().offset()); |
| ASSERT_EQ(displ.reference().base(), |
| op.displacement().reference().base()); |
| } |
| |
| } // namespace |
| |
| typedef BasicBlockAssemblerTest OperandTest; |
| |
| TEST_F(OperandTest, Factories) { |
| { |
| auto op(Operand(assm::eax)); |
| |
| ASSERT_EQ(assm::kRegisterEax, op.base()); |
| ASSERT_EQ(assm::kRegisterNone, op.index()); |
| ASSERT_EQ(assm::kTimes1, op.scale()); |
| ASSERT_EQ(assm::kSizeNone, op.displacement().size()); |
| |
| TestEqualDisplacement(op, Displacement()); |
| } |
| |
| { |
| // Register-indirect with displacement. |
| auto op(Operand(assm::eax, Displacement(100))); |
| ASSERT_EQ(assm::kRegisterEax, op.base()); |
| ASSERT_EQ(assm::kRegisterNone, op.index()); |
| ASSERT_EQ(assm::kTimes1, op.scale()); |
| ASSERT_EQ(assm::kSize8Bit, op.displacement().size()); |
| |
| TestEqualDisplacement(op, Displacement(100)); |
| |
| TestEqualDisplacement(Operand(assm::eax, Displacement(test_block_, 2)), |
| Displacement(test_block_, 2)); |
| TestEqualDisplacement(Operand(assm::eax, Displacement(test_bb_)), |
| Displacement(test_bb_)); |
| } |
| |
| { |
| // Displacement-only mode. |
| auto op(Operand(Displacement(100))); |
| ASSERT_EQ(assm::kRegisterNone, op.base()); |
| ASSERT_EQ(assm::kRegisterNone, op.index()); |
| ASSERT_EQ(assm::kTimes1, op.scale()); |
| ASSERT_EQ(assm::kSize8Bit, op.displacement().size()); |
| TestEqualDisplacement(op, Displacement(100)); |
| |
| TestEqualDisplacement(Operand(Displacement(test_block_, 2)), |
| Displacement(test_block_, 2)); |
| TestEqualDisplacement(Operand(Displacement(test_bb_)), |
| Displacement(test_bb_)); |
| } |
| |
| { |
| // The [base + index * scale] mode with displ. |
| auto op(Operand(assm::eax, assm::ebp, assm::kTimes2, Displacement(100))); |
| ASSERT_EQ(assm::kRegisterEax, op.base()); |
| ASSERT_EQ(assm::kRegisterEbp, op.index()); |
| ASSERT_EQ(assm::kTimes2, op.scale()); |
| ASSERT_EQ(assm::kSize8Bit, op.displacement().size()); |
| |
| TestEqualDisplacement( |
| Operand(assm::eax, assm::ebp, assm::kTimes2, |
| Displacement(test_block_, 2)), |
| Displacement(test_block_, 2)); |
| TestEqualDisplacement( |
| Operand(assm::eax, assm::ebp, assm::kTimes2, Displacement(test_bb_)), |
| Displacement(test_bb_)); |
| } |
| |
| { |
| // The [base + index * scale] mode - no displ. |
| auto op(Operand(assm::eax, assm::ebp, assm::kTimes2)); |
| ASSERT_EQ(assm::kRegisterEax, op.base()); |
| ASSERT_EQ(assm::kRegisterEbp, op.index()); |
| ASSERT_EQ(assm::kTimes2, op.scale()); |
| ASSERT_EQ(assm::kSizeNone, op.displacement().size()); |
| |
| // The [index * scale + displ32] mode - no base. |
| TestEqualDisplacement(Operand(assm::eax, assm::ebp, assm::kTimes2), |
| Displacement()); |
| } |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, nop) { |
| // We can't use ASSERT_NO_REFS here as nop may generate more than 1 |
| // instruction, an exception to the rule of 1 instruction that ASSERT_NO_REFS |
| // enforces. |
| asm_.nop(0); |
| ASSERT_EQ(0u, instructions_.size()); |
| |
| // Exactly 1 or 2 instructions should be emitted per NOP length from |
| // 1 to 15. |
| for (size_t i = 1; i <= 15; ++i) { |
| asm_.nop(i); |
| ASSERT_LT(0u, instructions_.size()); |
| ASSERT_GE(2u, instructions_.size()); |
| |
| // NOP instructions should have no references. |
| for (BasicCodeBlock::Instructions::const_iterator inst_it = |
| instructions_.begin(); |
| inst_it != instructions_.end(); |
| ++inst_it) { |
| ASSERT_EQ(0u, inst_it->references().size()); |
| } |
| instructions_.clear(); |
| } |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, call) { |
| asm_.call(Immediate(test_block_, 0)); |
| ASSERT_REFS(1, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| |
| asm_.call(Operand(Displacement(test_bb_))); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, jmp) { |
| asm_.jmp(Immediate(test_block_, 0)); |
| ASSERT_REFS(1, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| |
| asm_.jmp(Operand(Displacement(test_bb_))); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, mov_b) { |
| // mov BYTE PTR [base + index * scale + displ], immediate |
| asm_.mov_b(Operand(assm::eax, assm::ebx, assm::kTimes4, |
| Displacement(test_block_, 0)), |
| Immediate(10)); |
| ASSERT_REFS(3, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, movzx_b) { |
| // movzx eax, BYTE PTR [base + index * scale + displ] |
| asm_.movzx_b(assm::eax, |
| Operand(assm::eax, assm::ebx, assm::kTimes4, |
| Displacement(test_block_, 0))); |
| ASSERT_REFS(4, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, mov) { |
| // Simple register-register move. |
| asm_.mov(assm::eax, assm::ebx); |
| ASSERT_NO_REFS(); |
| |
| // Simple immediate-register move. |
| asm_.mov(assm::eax, Immediate(10)); |
| ASSERT_NO_REFS(); |
| |
| // Immediate-with reference to register. |
| asm_.mov(assm::eax, Immediate(test_block_, 0)); |
| ASSERT_REFS(1, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| |
| // Torture test; mov [displ], immediate, |
| // both src and dst contain references. |
| asm_.mov(Operand(Displacement(test_block_, 0)), Immediate(test_bb_)); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_, |
| 6, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| |
| // Torture test; mov [base + index * scale + displ], immediate, |
| // both src and dst contain references. |
| asm_.mov(Operand(assm::eax, assm::ebx, assm::kTimes4, |
| Displacement(test_block_, 0)), |
| Immediate(test_bb_)); |
| ASSERT_REFS(3, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_, |
| 7, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, mov_fs) { |
| asm_.mov_fs(Operand(assm::eax, assm::ebx, assm::kTimes4, |
| Displacement(test_block_, 0)), |
| assm::eax); |
| ASSERT_REFS(4, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| |
| asm_.mov_fs(assm::eax, |
| Operand(assm::eax, assm::ebx, assm::kTimes4, |
| Displacement(test_block_, 0))); |
| ASSERT_REFS(4, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, lea) { |
| asm_.lea(assm::eax, Operand(assm::eax)); |
| ASSERT_NO_REFS(); |
| |
| asm_.lea(assm::eax, |
| Operand(assm::eax, assm::ebx, assm::kTimes4, |
| Displacement(test_bb_))); |
| ASSERT_REFS(3, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, push) { |
| asm_.push(assm::esp); |
| ASSERT_NO_REFS(); |
| |
| asm_.push(Immediate(test_block_, 0)); |
| ASSERT_REFS(1, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| |
| asm_.push(Operand(assm::eax, assm::ebx, assm::kTimes4, |
| Displacement(test_bb_))); |
| ASSERT_REFS(3, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, pop) { |
| asm_.pop(assm::ebp); |
| ASSERT_NO_REFS(); |
| |
| asm_.pop(Operand(assm::eax, assm::ebx, assm::kTimes4, |
| Displacement(test_bb_))); |
| ASSERT_REFS(3, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, pushfd) { |
| asm_.pushfd(); |
| ASSERT_NO_REFS(); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, popfd) { |
| asm_.popfd(); |
| ASSERT_NO_REFS(); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, lahf) { |
| asm_.lahf(); |
| ASSERT_NO_REFS(); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, sahf) { |
| asm_.sahf(); |
| ASSERT_NO_REFS(); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, setxx) { |
| // Simple register-register operation. |
| asm_.set(assm::kParityEven, assm::eax); |
| ASSERT_NO_REFS(); |
| |
| asm_.set(assm::kOverflow, assm::ebx); |
| ASSERT_NO_REFS(); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, test) { |
| // Simple register-register operation. |
| asm_.test(assm::al, assm::bl); |
| ASSERT_NO_REFS(); |
| |
| // Simple immediate-register operation. |
| asm_.test(assm::al, Immediate(10, assm::kSize8Bit)); |
| ASSERT_NO_REFS(); |
| |
| // Simple register-register operation. |
| asm_.test(assm::eax, assm::ebx); |
| ASSERT_NO_REFS(); |
| |
| // Simple immediate-register operation. |
| asm_.test(assm::eax, Immediate(10)); |
| ASSERT_NO_REFS(); |
| |
| // Immediate-with reference to register. |
| asm_.test(assm::eax, Immediate(test_block_, 0)); |
| ASSERT_REFS(1, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| |
| // Torture test: both src and dst contain references. |
| asm_.test(Operand(Displacement(test_block_, 0)), Immediate(test_bb_)); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_, |
| 6, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| |
| asm_.test(Operand(Displacement(test_block_, 0)), Immediate(10)); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, cmp) { |
| // Simple register-register operation. |
| asm_.cmp(assm::al, assm::bl); |
| ASSERT_NO_REFS(); |
| |
| // Simple immediate-register operation. |
| asm_.cmp(assm::al, Immediate(10, assm::kSize8Bit)); |
| ASSERT_NO_REFS(); |
| |
| // Simple register-register operation. |
| asm_.cmp(assm::eax, assm::ebx); |
| ASSERT_NO_REFS(); |
| |
| // Simple immediate-register operation. |
| asm_.cmp(assm::eax, Immediate(10)); |
| ASSERT_NO_REFS(); |
| |
| // Immediate-with reference to register. |
| asm_.cmp(assm::eax, Immediate(test_block_, 0)); |
| ASSERT_REFS(1, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| |
| // Torture test: both src and dst contain references. |
| asm_.cmp(Operand(Displacement(test_block_, 0)), Immediate(test_bb_)); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_, |
| 6, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| |
| asm_.cmp(Operand(Displacement(test_block_, 0)), Immediate(10)); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, add) { |
| // Simple register-register operation. |
| asm_.add(assm::al, assm::bl); |
| ASSERT_NO_REFS(); |
| |
| // Simple immediate-register operation. |
| asm_.add(assm::al, Immediate(10, assm::kSize8Bit)); |
| ASSERT_NO_REFS(); |
| |
| // Simple register-register operation. |
| asm_.add(assm::eax, assm::ebx); |
| ASSERT_NO_REFS(); |
| |
| // Simple immediate-register operation. |
| asm_.add(assm::eax, Immediate(10)); |
| ASSERT_NO_REFS(); |
| |
| // Immediate-with reference to register. |
| asm_.add(assm::eax, Immediate(test_block_, 0)); |
| ASSERT_REFS(1, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| |
| // Torture test: both src and dst contain references. |
| asm_.add(Operand(Displacement(test_block_, 0)), Immediate(test_bb_)); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_, |
| 6, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| |
| asm_.add(Operand(Displacement(test_block_, 0)), Immediate(10)); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, sub) { |
| // Simple register-register operation. |
| asm_.sub(assm::al, assm::bl); |
| ASSERT_NO_REFS(); |
| |
| // Simple immediate-register operation. |
| asm_.sub(assm::al, Immediate(10, assm::kSize8Bit)); |
| ASSERT_NO_REFS(); |
| |
| // Simple register-register operation. |
| asm_.sub(assm::eax, assm::ebx); |
| ASSERT_NO_REFS(); |
| |
| // Simple immediate-register operation. |
| asm_.sub(assm::eax, Immediate(10)); |
| ASSERT_NO_REFS(); |
| |
| // Immediate-with reference to register. |
| asm_.sub(assm::eax, Immediate(test_block_, 0)); |
| ASSERT_REFS(1, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| |
| // Torture test: both src and dst contain references. |
| asm_.sub(Operand(Displacement(test_block_, 0)), Immediate(test_bb_)); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_, |
| 6, BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK, test_bb_); |
| |
| asm_.sub(Operand(Displacement(test_block_, 0)), Immediate(10)); |
| ASSERT_REFS(2, BasicBlockReference::REFERRED_TYPE_BLOCK, test_block_); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, shl) { |
| // Simple immediate-register operation. |
| asm_.shl(assm::eax, Immediate(1)); |
| ASSERT_NO_REFS(); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, shr) { |
| // Simple immediate-register operation. |
| asm_.shr(assm::eax, Immediate(1)); |
| ASSERT_NO_REFS(); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, ret) { |
| asm_.ret(); |
| ASSERT_NO_REFS(); |
| |
| asm_.ret(4); |
| ASSERT_NO_REFS(); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, xchg) { |
| asm_.xchg(assm::eax, assm::ecx); |
| ASSERT_NO_REFS(); |
| |
| asm_.xchg(assm::esp, assm::edx); |
| ASSERT_NO_REFS(); |
| |
| asm_.xchg(assm::ax, assm::cx); |
| ASSERT_NO_REFS(); |
| |
| asm_.xchg(assm::sp, assm::dx); |
| ASSERT_NO_REFS(); |
| |
| asm_.xchg(assm::al, assm::ch); |
| ASSERT_NO_REFS(); |
| |
| asm_.xchg(assm::dh, assm::bl); |
| ASSERT_NO_REFS(); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, UndefinedSourceRange) { |
| ASSERT_EQ(asm_.source_range(), SourceRange()); |
| asm_.call(Immediate(test_block_, 0)); |
| ASSERT_EQ(instructions_.back().source_range(), SourceRange()); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, SetSourceRange) { |
| SourceRange range(RelativeAddress(10), 10); |
| asm_.set_source_range(range); |
| asm_.call(Immediate(test_block_, 0)); |
| ASSERT_EQ(instructions_.back().source_range(), range); |
| } |
| |
| TEST_F(BasicBlockAssemblerTest, SetMultipleSourceRange) { |
| SourceRange range1(RelativeAddress(10), 10); |
| SourceRange range2(RelativeAddress(20), 20); |
| |
| asm_.set_source_range(range1); |
| asm_.call(Immediate(test_block_, 0)); |
| ASSERT_EQ(instructions_.back().source_range(), range1); |
| |
| asm_.set_source_range(range2); |
| asm_.pop(assm::ebp); |
| ASSERT_EQ(instructions_.back().source_range(), range2); |
| |
| asm_.ret(4); |
| ASSERT_EQ(instructions_.back().source_range(), range2); |
| } |
| |
| } // namespace block_graph |