1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // Implementation of disassembler.
16 : #include "syzygy/core/disassembler.h"
17 :
18 : #include <vector>
19 :
20 : #include "base/bind.h"
21 : #include "base/memory/scoped_ptr.h"
22 : #include "gmock/gmock.h"
23 : #include "gtest/gtest.h"
24 :
25 : using testing::_;
26 : using testing::Invoke;
27 : using testing::Return;
28 :
29 : extern "C" {
30 :
31 : // functions and labels exposed from our .asm test stub.
32 : extern int assembly_func();
33 : extern int internal_label();
34 : extern int assembly_func_end();
35 :
36 : extern int assembly_switch();
37 : extern int case_0();
38 : extern int case_1();
39 : extern int case_default();
40 : extern int jump_table();
41 : extern int lookup_table();
42 : extern int assembly_switch_end();
43 :
44 : // Functions invoked or referred by the .asm test stub.
45 i : int func1() {
46 i : return 1;
47 i : }
48 :
49 i : int func2() {
50 i : return 2;
51 i : }
52 :
53 i : int func3() {
54 i : return 3;
55 i : }
56 :
57 i : int func4() {
58 i : return 4;
59 i : }
60 :
61 : } // extern "C"
62 :
63 : namespace core {
64 :
65 : class DisassemblerTest: public testing::Test {
66 : public:
67 E : virtual void SetUp() {
68 : on_instruction_ = base::Bind(&DisassemblerTest::OnInstruction,
69 E : base::Unretained(this));
70 E : }
71 :
72 : MOCK_METHOD2(OnInstruction,
73 : Disassembler::CallbackDirective(const Disassembler&,
74 E : const _DInst&));
75 :
76 E : static AbsoluteAddress AddressOf(const void* ptr) {
77 E : return AbsoluteAddress(reinterpret_cast<size_t>(ptr));
78 E : }
79 :
80 E : static const uint8* PointerTo(const void* ptr) {
81 E : return reinterpret_cast<const uint8*>(ptr);
82 E : }
83 :
84 : Disassembler::CallbackDirective RecordFunctionEncounter(
85 E : const Disassembler& disasm, const _DInst& inst) {
86 E : switch (META_GET_FC(inst.meta)) {
87 : case FC_CALL:
88 : case FC_UNC_BRANCH:
89 E : EXPECT_EQ(O_PC, inst.ops[0].type);
90 E : if (inst.ops[0].size == 8) {
91 E : EXPECT_EQ(2, inst.size);
92 E : } else {
93 E : EXPECT_EQ(32, inst.ops[0].size);
94 E : EXPECT_EQ(5, inst.size);
95 : functions_.push_back(
96 : AbsoluteAddress(
97 E : static_cast<size_t>(inst.addr + inst.size + inst.imm.addr)));
98 : }
99 : break;
100 : default:
101 : break;
102 : }
103 E : return Disassembler::kDirectiveContinue;
104 E : }
105 :
106 : protected:
107 : std::vector<AbsoluteAddress> functions_;
108 :
109 : Disassembler::InstructionCallback on_instruction_;
110 : };
111 :
112 E : TEST_F(DisassemblerTest, Terminate) {
113 : Disassembler disasm(PointerTo(&assembly_func),
114 : PointerTo(&assembly_func_end) - PointerTo(&assembly_func),
115 : AddressOf(&assembly_func),
116 E : on_instruction_);
117 E : ASSERT_TRUE(disasm.Unvisited(AddressOf(&assembly_func)));
118 :
119 : // Terminate the walk on first visit.
120 : EXPECT_CALL(*this, OnInstruction(_, _)).
121 E : WillRepeatedly(Return(Disassembler::kDirectiveTerminateWalk));
122 :
123 E : ASSERT_EQ(Disassembler::kWalkTerminated, disasm.Walk());
124 E : }
125 :
126 E : TEST_F(DisassemblerTest, DisassemblePartial) {
127 : Disassembler disasm(PointerTo(&assembly_func),
128 : PointerTo(&assembly_func_end) - PointerTo(&assembly_func),
129 : AddressOf(&assembly_func),
130 E : on_instruction_);
131 E : ASSERT_TRUE(disasm.Unvisited(AddressOf(&assembly_func)));
132 :
133 : // We should hit 6 instructions.
134 : EXPECT_CALL(*this, OnInstruction(_, _)).Times(6).
135 E : WillRepeatedly(Return(Disassembler::kDirectiveContinue));
136 :
137 E : ASSERT_EQ(Disassembler::kWalkSuccess, disasm.Walk());
138 : // We should have disassembled everything save one call to func3 and
139 : // the jump/lookup tables.
140 : ASSERT_EQ(PointerTo(&assembly_func_end) - PointerTo(&assembly_func) - 5,
141 E : disasm.disassembled_bytes());
142 E : }
143 :
144 E : TEST_F(DisassemblerTest, DisassembleFull) {
145 : Disassembler disasm(PointerTo(&assembly_func),
146 : PointerTo(&assembly_func_end) - PointerTo(&assembly_func),
147 : AddressOf(&assembly_func),
148 E : on_instruction_);
149 E : ASSERT_TRUE(disasm.Unvisited(AddressOf(&assembly_func)));
150 : // Mark the internal label as well.
151 E : ASSERT_TRUE(disasm.Unvisited(AddressOf(&internal_label)));
152 :
153 : // We should hit 7 instructions.
154 : EXPECT_CALL(*this, OnInstruction(_, _)).Times(7).
155 E : WillRepeatedly(Return(Disassembler::kDirectiveContinue));
156 :
157 E : ASSERT_EQ(Disassembler::kWalkSuccess, disasm.Walk());
158 :
159 : // We should have disassembled everything.
160 : ASSERT_EQ(PointerTo(&assembly_func_end) - PointerTo(&assembly_func),
161 E : disasm.disassembled_bytes());
162 E : }
163 :
164 E : TEST_F(DisassemblerTest, EncounterFunctions) {
165 : Disassembler disasm(PointerTo(&assembly_func),
166 : PointerTo(&assembly_func_end) - PointerTo(&assembly_func),
167 : AddressOf(&assembly_func),
168 E : on_instruction_);
169 E : ASSERT_TRUE(disasm.Unvisited(AddressOf(&assembly_func)));
170 : // Mark the internal label as well.
171 E : ASSERT_TRUE(disasm.Unvisited(AddressOf(&internal_label)));
172 :
173 : // Record the functions we encounter along the way.
174 : EXPECT_CALL(*this, OnInstruction(_, _)).
175 : WillRepeatedly(Invoke(this,
176 E : &DisassemblerTest::RecordFunctionEncounter));
177 :
178 E : ASSERT_EQ(Disassembler::kWalkSuccess, disasm.Walk());
179 :
180 E : std::vector<AbsoluteAddress> expected;
181 E : expected.push_back(AddressOf(func1));
182 E : expected.push_back(AddressOf(func2));
183 E : expected.push_back(AddressOf(func3));
184 E : expected.push_back(AddressOf(func4));
185 :
186 E : EXPECT_THAT(functions_, testing::ContainerEq(expected));
187 E : }
188 :
189 E : TEST_F(DisassemblerTest, StopsAtTerminateNoReturnFunctionCall) {
190 : Disassembler disasm(
191 : PointerTo(&assembly_switch),
192 : PointerTo(&assembly_switch_end) - PointerTo(&assembly_switch),
193 E : AddressOf(&assembly_switch), on_instruction_);
194 :
195 : // Mark the entry of the case that calls a non-returning function
196 E : ASSERT_TRUE(disasm.Unvisited(AddressOf(&case_default)));
197 :
198 : // We expect to hit all the instructions in the case
199 : // "case_default" from disassembler_test_code.asm.
200 : EXPECT_CALL(*this, OnInstruction(_, _))
201 : .Times(2)
202 : .WillOnce(Return(Disassembler::kDirectiveContinue))
203 E : .WillOnce(Return(Disassembler::kDirectiveTerminatePath));
204 :
205 : // We expect a complete walk from this, as there are no branches to
206 : // chase down
207 E : ASSERT_EQ(Disassembler::kWalkSuccess, disasm.Walk());
208 E : }
209 :
210 : } // namespace image_util
|