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 : #include "syzygy/pe/dia_browser.h"
15 :
16 : #include <diacreate.h>
17 :
18 : #include "base/bind.h"
19 : #include "base/path_service.h"
20 : #include "base/files/file_path.h"
21 : #include "base/memory/scoped_ptr.h"
22 : #include "base/win/scoped_comptr.h"
23 : #include "gmock/gmock.h"
24 : #include "gtest/gtest.h"
25 : #include "syzygy/core/unittest_util.h"
26 : #include "syzygy/pe/dia_util.h"
27 :
28 : using base::win::ScopedComPtr;
29 : using testing::_;
30 : using testing::Return;
31 :
32 : namespace pe {
33 :
34 : using builder::Callback;
35 : using builder::Not;
36 : using builder::Opt;
37 : using builder::Or;
38 : using builder::Plus;
39 : using builder::Seq;
40 : using builder::Star;
41 : using builder::Tag;
42 : using builder::Tags;
43 :
44 : class PatternTest: public testing::Test {
45 : public:
46 E : PatternTest() {
47 E : }
48 :
49 : MOCK_METHOD3(OnMatch, DiaBrowser::BrowserDirective(
50 : const DiaBrowser&,
51 : const DiaBrowser::SymTagVector&,
52 i : const DiaBrowser::SymbolPtrVector&));
53 :
54 E : virtual void SetUp() {
55 E : on_match_ = base::Bind(&PatternTest::OnMatch, base::Unretained(this));
56 E : }
57 :
58 : protected:
59 : DiaBrowser::MatchCallback on_match_;
60 : };
61 :
62 : const wchar_t kPdbName[] = L"syzygy\\pe\\test_data\\test_dll.pdb";
63 :
64 : class DiaBrowserTest: public testing::Test {
65 : public:
66 :
67 E : DiaBrowserTest() {
68 E : }
69 :
70 : DiaBrowser::BrowserDirective OnPartialMatchTerminate(
71 : const DiaBrowser& dia_browser,
72 : const DiaBrowser::SymTagVector& tag_lineage,
73 E : const DiaBrowser::SymbolPtrVector& symbol_lineage) {
74 : // Call the 'OnPartialMatch' for bookkeeping reasons.
75 E : OnPartialMatch(dia_browser, tag_lineage, symbol_lineage);
76 E : if (!tag_lineage.empty() && tag_lineage.back() == SymTagUDT)
77 E : return DiaBrowser::kBrowserTerminatePath;
78 E : return DiaBrowser::kBrowserContinue;
79 E : }
80 :
81 : MOCK_METHOD3(OnPartialMatch, DiaBrowser::BrowserDirective(
82 : const DiaBrowser& dia_browser,
83 : const DiaBrowser::SymTagVector& tag_lineage,
84 E : const DiaBrowser::SymbolPtrVector& symbol_lineage));
85 :
86 : MOCK_METHOD3(OnFullMatch, DiaBrowser::BrowserDirective(
87 : const DiaBrowser& dia_browser,
88 : const DiaBrowser::SymTagVector& tag_lineage,
89 E : const DiaBrowser::SymbolPtrVector& symbol_lineage));
90 :
91 E : virtual void SetUp() {
92 : on_partial_match_term_ =
93 : base::Bind(&DiaBrowserTest::OnPartialMatchTerminate,
94 E : base::Unretained(this));
95 : on_partial_match_ = base::Bind(&DiaBrowserTest::OnPartialMatch,
96 E : base::Unretained(this));
97 : on_full_match_ = base::Bind(&DiaBrowserTest::OnFullMatch,
98 E : base::Unretained(this));
99 :
100 E : if (!SUCCEEDED(dia_source_.CreateInstance(CLSID_DiaSource)))
101 E : ASSERT_HRESULT_SUCCEEDED(NoRegCoCreate(
102 : pe::kDiaDllName, CLSID_DiaSource, IID_IDiaDataSource,
103 : reinterpret_cast<void**>(&dia_source_)));
104 :
105 : HRESULT hr = dia_source_->loadDataFromPdb(
106 E : testing::GetSrcRelativePath(kPdbName).value().c_str());
107 E : ASSERT_HRESULT_SUCCEEDED(hr);
108 :
109 E : hr = dia_source_->openSession(dia_session_.Receive());
110 E : ASSERT_HRESULT_SUCCEEDED(hr);
111 :
112 E : hr = dia_session_->get_globalScope(global_.Receive());
113 E : ASSERT_HRESULT_SUCCEEDED(hr);
114 E : }
115 :
116 E : virtual void TearDown() {
117 E : global_.Release();
118 E : dia_session_.Release();
119 E : dia_source_.Release();
120 E : }
121 :
122 : protected:
123 : DiaBrowser::MatchCallback on_partial_match_term_;
124 : DiaBrowser::MatchCallback on_partial_match_;
125 : DiaBrowser::MatchCallback on_full_match_;
126 : ScopedComPtr<IDiaDataSource> dia_source_;
127 : ScopedComPtr<IDiaSession> dia_session_;
128 : ScopedComPtr<IDiaSymbol> global_;
129 : };
130 :
131 : } // namespace
132 :
133 : namespace pe {
134 :
135 : class TestDiaBrowser : public DiaBrowser {
136 : public:
137 : using DiaBrowser::TestMatch;
138 : };
139 :
140 E : TEST_F(PatternTest, NullMatchingPatternIsInvalid) {
141 E : TestDiaBrowser dia_browser;
142 :
143 : // The pattern 'Compiland?' would match everything with a null match,
144 : // so it should be rejected.
145 E : EXPECT_FALSE(dia_browser.AddPattern(Opt(SymTagCompiland), on_match_));
146 E : }
147 :
148 E : TEST_F(PatternTest, Wildcard) {
149 E : TestDiaBrowser dia_browser;
150 :
151 E : ASSERT_TRUE(dia_browser.AddPattern(Tag(SymTagNull), on_match_));
152 :
153 : // The wild-card pattern should match every sym tag.
154 E : for (size_t i = kSymTagBegin; i < kSymTagEnd; ++i) {
155 E : SymTag sym_tag = static_cast<SymTag>(i);
156 E : std::vector<SymTag> sym_tags(1, sym_tag);
157 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
158 E : }
159 E : }
160 :
161 E : TEST_F(PatternTest, Seq) {
162 E : TestDiaBrowser dia_browser;
163 :
164 : // Set up pattern 'Compiland.Function.Block.Data'.
165 : ASSERT_TRUE(dia_browser.AddPattern(
166 : Seq(SymTagCompiland, SymTagFunction, SymTagBlock, SymTagData),
167 E : on_match_));
168 :
169 : // This exact pattern should match, but not any prefix of it, nor any
170 : // longer pattern containing it as a prefix.
171 E : std::vector<SymTag> sym_tags;
172 E : sym_tags.push_back(SymTagCompiland);
173 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
174 E : sym_tags.push_back(SymTagFunction);
175 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
176 E : sym_tags.push_back(SymTagBlock);
177 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
178 E : sym_tags.push_back(SymTagData);
179 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
180 E : sym_tags.push_back(SymTagData);
181 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
182 E : }
183 :
184 E : TEST_F(PatternTest, EmptySymTagBitSetRejected) {
185 E : TestDiaBrowser dia_browser;
186 :
187 : // A pattern with an element that can't match anything should be rejected.
188 E : EXPECT_FALSE(dia_browser.AddPattern(Not(SymTagNull), on_match_));
189 E : EXPECT_FALSE(dia_browser.AddPattern(Tags(SymTagBitSet()), on_match_));
190 E : }
191 :
192 E : TEST_F(PatternTest, Not) {
193 E : TestDiaBrowser dia_browser;
194 :
195 : // Set up pattern '[^Compiland]'
196 E : ASSERT_TRUE(dia_browser.AddPattern(Not(SymTagCompiland), on_match_));
197 :
198 : // This should match every SymTag *except* Compiland.
199 E : for (size_t i = kSymTagBegin; i < kSymTagEnd; ++i) {
200 E : SymTag sym_tag = static_cast<SymTag>(i);
201 E : std::vector<SymTag> sym_tags(1, sym_tag);
202 E : if (i == SymTagCompiland)
203 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
204 E : else
205 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
206 E : }
207 E : }
208 :
209 E : TEST_F(PatternTest, MultiArgNot) {
210 E : TestDiaBrowser dia_browser;
211 :
212 : // The multi-arg versions accept up to 8 inputs currently. Test
213 : // with a full set of inputs. The sym tags used here are consecutive
214 : // from kSymTagBegin.
215 : ASSERT_TRUE(dia_browser.AddPattern(Not(SymTagExe,
216 : SymTagCompiland,
217 : SymTagCompilandDetails,
218 : SymTagCompilandEnv,
219 : SymTagFunction,
220 : SymTagBlock,
221 : SymTagData,
222 : SymTagAnnotation),
223 E : on_match_));
224 :
225 : // Ensure the pattern doesn't match the first 8 symtags, but matches all
226 : // the rest.
227 E : for (size_t i = kSymTagBegin; i < kSymTagEnd; ++i) {
228 E : SymTag sym_tag = static_cast<SymTag>(i);
229 E : std::vector<SymTag> sym_tags(1, sym_tag);
230 E : if (i <= SymTagAnnotation)
231 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
232 E : else
233 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
234 E : }
235 E : }
236 :
237 E : TEST_F(PatternTest, MultiArgTags) {
238 E : TestDiaBrowser dia_browser;
239 :
240 : // The multi-arg versions accept up to 8 inputs currently. Test
241 : // with a full set of inputs. The sym tags used here are consecutive
242 : // from kSymTagBegin.
243 : ASSERT_TRUE(dia_browser.AddPattern(Tags(SymTagExe,
244 : SymTagCompiland,
245 : SymTagCompilandDetails,
246 : SymTagCompilandEnv,
247 : SymTagFunction,
248 : SymTagBlock,
249 : SymTagData,
250 : SymTagAnnotation),
251 E : on_match_));
252 :
253 : // Ensure the pattern matches the first 8 symtags, but does not match all
254 : // the rest.
255 E : for (size_t i = kSymTagBegin; i < kSymTagEnd; ++i) {
256 E : SymTag sym_tag = static_cast<SymTag>(i);
257 E : std::vector<SymTag> sym_tags(1, sym_tag);
258 E : if (i <= SymTagAnnotation)
259 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
260 E : else
261 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
262 E : }
263 E : }
264 :
265 E : TEST_F(PatternTest, Opt) {
266 E : TestDiaBrowser dia_browser;
267 :
268 : // Set up pattern 'Compiland?.Function'.
269 : ASSERT_TRUE(dia_browser.AddPattern(
270 E : Seq(Opt(SymTagCompiland), SymTagFunction), on_match_));
271 :
272 E : std::vector<SymTag> sym_tags;
273 :
274 : // 'Compiland' should not match.
275 E : sym_tags.push_back(SymTagCompiland);
276 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
277 :
278 : // 'Compiland.Function' should match.
279 E : sym_tags.push_back(SymTagFunction);
280 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
281 :
282 : // 'Function' should match.
283 E : sym_tags.clear();
284 E : sym_tags.push_back(SymTagFunction);
285 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
286 E : }
287 :
288 E : TEST_F(PatternTest, Star) {
289 E : TestDiaBrowser dia_browser;
290 :
291 : // Set up pattern 'Compiland.Block*.Data'.
292 : ASSERT_TRUE(dia_browser.AddPattern(
293 E : Seq(SymTagCompiland, Star(SymTagBlock), SymTagData), on_match_));
294 :
295 E : std::vector<SymTag> sym_tags;
296 :
297 : // 'Compiland' should not match.
298 E : sym_tags.push_back(SymTagCompiland);
299 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
300 :
301 : // 'Compiland.Data' should match.
302 E : sym_tags.push_back(SymTagData);
303 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
304 :
305 : // 'Compiland.Block.Data' should match, with Block repeated
306 : // arbitrarily many times. We can only check a finite number, obviously.
307 E : for (size_t i = 0; i < 10; ++i) {
308 E : sym_tags.pop_back();
309 E : sym_tags.push_back(SymTagBlock);
310 E : sym_tags.push_back(SymTagData);
311 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
312 E : }
313 E : }
314 :
315 E : TEST_F(PatternTest, Plus) {
316 E : TestDiaBrowser dia_browser;
317 :
318 : // Set up pattern 'Compiland.Block+.Data'.
319 : ASSERT_TRUE(dia_browser.AddPattern(
320 E : Seq(SymTagCompiland, Plus(SymTagBlock), SymTagData), on_match_));
321 :
322 E : std::vector<SymTag> sym_tags;
323 :
324 : // 'Compiland' should not match'.
325 E : sym_tags.push_back(SymTagCompiland);
326 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
327 :
328 : // 'Compiland.Data' should not match.
329 E : sym_tags.push_back(SymTagData);
330 E : EXPECT_EQ(0, dia_browser.TestMatch(sym_tags));
331 :
332 : // 'Compiland.Block.Data' should match, with Block repeated
333 : // arbitrarily many times. We can only check a finite number, obviously.
334 E : for (size_t i = 0; i < 10; ++i) {
335 E : sym_tags.pop_back();
336 E : sym_tags.push_back(SymTagBlock);
337 E : sym_tags.push_back(SymTagData);
338 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
339 E : }
340 E : }
341 :
342 E : TEST_F(PatternTest, Or) {
343 E : TestDiaBrowser dia_browser;
344 :
345 : // Add an 'or' pattern that uses all 8 arguments.
346 : ASSERT_TRUE(dia_browser.AddPattern(
347 : Or(SymTagCompiland,
348 : Seq(SymTagData, SymTagCompiland, SymTagExe),
349 : Seq(SymTagExe, SymTagCompiland),
350 : Seq(SymTagExe, SymTagData),
351 : Seq(SymTagExe, SymTagExe),
352 : Seq(SymTagLabel, SymTagCompiland),
353 : Seq(SymTagLabel, SymTagLabel, SymTagLabel),
354 : SymTagVTable),
355 E : on_match_));
356 :
357 E : std::vector<SymTag> sym_tags;
358 E : sym_tags.push_back(SymTagCompiland);
359 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
360 :
361 E : sym_tags.clear();
362 E : sym_tags.push_back(SymTagData);
363 E : sym_tags.push_back(SymTagCompiland);
364 E : sym_tags.push_back(SymTagExe);
365 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
366 :
367 E : sym_tags.clear();
368 E : sym_tags.push_back(SymTagExe);
369 E : sym_tags.push_back(SymTagCompiland);
370 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
371 :
372 E : sym_tags.back() = SymTagData;
373 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
374 :
375 E : sym_tags.back() = SymTagExe;
376 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
377 :
378 E : sym_tags.clear();
379 E : sym_tags.push_back(SymTagLabel);
380 E : sym_tags.push_back(SymTagCompiland);
381 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
382 :
383 E : sym_tags.back() = SymTagLabel;
384 E : sym_tags.push_back(SymTagLabel);
385 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
386 :
387 E : sym_tags.clear();
388 E : sym_tags.push_back(SymTagVTable);
389 E : EXPECT_EQ(1, dia_browser.TestMatch(sym_tags));
390 E : }
391 :
392 E : TEST_F(DiaBrowserTest, AllCompilandSymbolsExplored) {
393 E : TestDiaBrowser dia_browser;
394 :
395 E : dia_browser.AddPattern(Tag(SymTagCompiland), on_full_match_);
396 :
397 : EXPECT_CALL(*this, OnFullMatch(_, _, _)).Times(154).
398 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
399 E : dia_browser.Browse(global_.get());
400 E : }
401 :
402 E : TEST_F(DiaBrowserTest, AllDataSymbolsExplored) {
403 E : TestDiaBrowser dia_browser;
404 :
405 : // Search for (Wildcard)*.Data
406 : dia_browser.AddPattern(Seq(Star(SymTagNull), SymTagData),
407 E : on_full_match_);
408 :
409 : EXPECT_CALL(*this, OnFullMatch(_, _, _)).Times(2883).
410 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
411 E : dia_browser.Browse(global_.get());
412 E : }
413 :
414 E : TEST_F(DiaBrowserTest, AllDataSymbolsExploredWithPopCallbacks) {
415 E : TestDiaBrowser dia_browser;
416 :
417 : // Search for (Wildcard)*.Data
418 : dia_browser.AddPattern(Seq(Star(SymTagNull), SymTagData),
419 E : on_full_match_, on_full_match_);
420 :
421 : EXPECT_CALL(*this, OnFullMatch(_, _, _)).Times(2 * 2883).
422 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
423 E : dia_browser.Browse(global_.get());
424 E : }
425 :
426 E : TEST_F(DiaBrowserTest, SomePathsTerminated) {
427 E : TestDiaBrowser dia_browser;
428 :
429 : // Search for UDT.Data and Enum.Data. This will find:
430 : // 428 Enum.Data
431 : // 1077 UDT.Data
432 : // However, we terminate partial matches when they get to
433 : // UDT.
434 : dia_browser.AddPattern(Seq(Callback(Or(SymTagEnum, SymTagUDT),
435 : on_partial_match_term_),
436 : SymTagData),
437 E : on_full_match_);
438 :
439 : // With VC++ 2010, OnPartialMatch hits 174 nodes.
440 : static const size_t kNumPartialMatches = 174;
441 :
442 : // Only the 428 Enum.Data full matches should be hit.
443 : static const size_t kNumFullMatches = 428;
444 :
445 : EXPECT_CALL(*this, OnPartialMatch(_, _, _)).Times(kNumPartialMatches).
446 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
447 : EXPECT_CALL(*this, OnFullMatch(_, _, _)).Times(kNumFullMatches).
448 E : WillRepeatedly(Return(DiaBrowser::kBrowserContinue));
449 E : dia_browser.Browse(global_.get());
450 E : }
451 :
452 : } // namespace pe
|