1 : // Copyright 2011 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 : #include "syzygy/pe/dia_util.h"
16 :
17 : #include <map>
18 : #include <set>
19 : #include <vector>
20 :
21 : #include "base/bind.h"
22 : #include "base/files/file_path.h"
23 : #include "base/files/file_util.h"
24 : #include "base/win/scoped_bstr.h"
25 : #include "base/win/scoped_comptr.h"
26 : #include "gmock/gmock.h"
27 : #include "syzygy/core/file_util.h"
28 : #include "syzygy/core/unittest_util.h"
29 : #include "syzygy/pdb/pdb_data.h"
30 : #include "syzygy/pe/unittest_util.h"
31 :
32 : namespace pe {
33 :
34 : namespace {
35 :
36 : using base::win::ScopedBstr;
37 : using base::win::ScopedComPtr;
38 :
39 : typedef std::vector<std::wstring> StringVector;
40 :
41 : static const wchar_t kNonsenseStreamName[] =
42 : L"ThisStreamNameCertainlyDoesNotExist";
43 :
44 : // The test_dll output file name configured by the build system.
45 : static const char kTestDllObjPath[] = TEST_DLL_OBJECT_FILE;
46 :
47 : struct FilePathLess {
48 E : bool operator()(const std::wstring& lhs, const std::wstring& rhs) const {
49 E : return base::FilePath::CompareLessIgnoreCase(lhs, rhs);
50 E : }
51 : };
52 :
53 : class DiaUtilTest : public testing::PELibUnitTest {
54 : };
55 :
56 : bool PathsAreEquivalent(const base::FilePath::StringType& path1_val,
57 E : const base::FilePath::StringType& path2_val) {
58 E : base::FilePath path1(path1_val);
59 E : base::FilePath path2(path2_val);
60 E : core::FilePathCompareResult result = core::CompareFilePaths(path1, path2);
61 E : return result == core::kEquivalentFilePaths;
62 E : }
63 :
64 E : MATCHER_P(IsSameFile, value, "") {
65 E : return PathsAreEquivalent(arg, value);
66 E : }
67 :
68 : bool StringVectorContainsPath(const StringVector& string_vector,
69 E : const base::FilePath::StringType& path) {
70 E : for (const auto& iter : string_vector) {
71 E : if (PathsAreEquivalent(iter, path))
72 E : return true;
73 i : }
74 i : return false;
75 E : }
76 :
77 : } // namespace
78 :
79 E : TEST_F(DiaUtilTest, CreateDiaSource) {
80 E : ScopedComPtr<IDiaDataSource> dia_source;
81 E : EXPECT_TRUE(CreateDiaSource(dia_source.Receive()));
82 E : }
83 :
84 E : TEST_F(DiaUtilTest, CreateDiaSesssionDll) {
85 E : ScopedComPtr<IDiaDataSource> dia_source;
86 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
87 :
88 E : ScopedComPtr<IDiaSession> dia_session;
89 E : EXPECT_TRUE(CreateDiaSession(
90 : testing::GetExeRelativePath(testing::kTestDllName),
91 : dia_source.get(),
92 E : dia_session.Receive()));
93 E : }
94 :
95 E : TEST_F(DiaUtilTest, CreateDiaSesssionPdb) {
96 E : ScopedComPtr<IDiaDataSource> dia_source;
97 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
98 :
99 E : ScopedComPtr<IDiaSession> dia_session;
100 E : EXPECT_TRUE(CreateDiaSession(
101 : testing::GetExeRelativePath(testing::kTestDllPdbName),
102 : dia_source.get(),
103 E : dia_session.Receive()));
104 E : }
105 :
106 E : TEST_F(DiaUtilTest, FindDiaTableByIid) {
107 E : ScopedComPtr<IDiaDataSource> dia_source;
108 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
109 :
110 E : ScopedComPtr<IDiaSession> dia_session;
111 E : ASSERT_TRUE(CreateDiaSession(
112 : testing::GetExeRelativePath(testing::kTestDllPdbName),
113 : dia_source.get(),
114 E : dia_session.Receive()));
115 :
116 E : ScopedComPtr<IDiaEnumSectionContribs> section_contribs;
117 E : EXPECT_EQ(kSearchSucceeded,
118 : FindDiaTable(section_contribs.iid(),
119 : dia_session.get(),
120 E : reinterpret_cast<void**>(section_contribs.Receive())));
121 E : }
122 :
123 E : TEST_F(DiaUtilTest, FindDiaTableByType) {
124 E : ScopedComPtr<IDiaDataSource> dia_source;
125 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
126 :
127 E : ScopedComPtr<IDiaSession> dia_session;
128 E : ASSERT_TRUE(CreateDiaSession(
129 : testing::GetExeRelativePath(testing::kTestDllPdbName),
130 : dia_source.get(),
131 E : dia_session.Receive()));
132 :
133 E : ScopedComPtr<IDiaEnumSectionContribs> section_contribs;
134 E : EXPECT_EQ(kSearchSucceeded,
135 E : FindDiaTable(dia_session.get(), section_contribs.Receive()));
136 E : }
137 :
138 E : TEST_F(DiaUtilTest, FindDiaDebugStream) {
139 E : ScopedComPtr<IDiaDataSource> dia_source;
140 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
141 :
142 E : ScopedComPtr<IDiaSession> dia_session;
143 E : ASSERT_TRUE(CreateDiaSession(
144 : testing::GetExeRelativePath(testing::kTestDllPdbName),
145 : dia_source.get(),
146 E : dia_session.Receive()));
147 :
148 E : ScopedComPtr<IDiaEnumDebugStreamData> debug_stream;
149 :
150 E : EXPECT_EQ(kSearchFailed,
151 : FindDiaDebugStream(kNonsenseStreamName,
152 : dia_session.get(),
153 E : debug_stream.Receive()));
154 :
155 E : EXPECT_EQ(kSearchSucceeded,
156 : FindDiaDebugStream(kFixupDiaDebugStreamName,
157 : dia_session.get(),
158 E : debug_stream.Receive()));
159 E : }
160 :
161 E : TEST_F(DiaUtilTest, LoadDiaDebugStream) {
162 E : ScopedComPtr<IDiaDataSource> dia_source;
163 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
164 :
165 E : ScopedComPtr<IDiaSession> dia_session;
166 E : ASSERT_TRUE(CreateDiaSession(
167 : testing::GetExeRelativePath(testing::kTestDllPdbName),
168 : dia_source.get(),
169 E : dia_session.Receive()));
170 :
171 E : ScopedComPtr<IDiaEnumDebugStreamData> debug_stream;
172 E : ASSERT_EQ(kSearchSucceeded,
173 : FindDiaDebugStream(kFixupDiaDebugStreamName,
174 : dia_session.get(),
175 E : debug_stream.Receive()));
176 :
177 E : std::vector<pdb::PdbFixup> fixups;
178 E : EXPECT_TRUE(LoadDiaDebugStream(debug_stream.get(), &fixups));
179 E : EXPECT_FALSE(fixups.empty());
180 E : }
181 :
182 E : TEST_F(DiaUtilTest, FindAndLoadDiaDebugStreamByName) {
183 E : ScopedComPtr<IDiaDataSource> dia_source;
184 E : ASSERT_TRUE(CreateDiaSource(dia_source.Receive()));
185 :
186 E : ScopedComPtr<IDiaSession> dia_session;
187 E : ASSERT_TRUE(CreateDiaSession(
188 : testing::GetExeRelativePath(testing::kTestDllPdbName),
189 : dia_source.get(),
190 E : dia_session.Receive()));
191 :
192 E : std::vector<pdb::PdbFixup> fixups;
193 :
194 E : EXPECT_EQ(kSearchFailed,
195 : FindAndLoadDiaDebugStreamByName(kNonsenseStreamName,
196 : dia_session.get(),
197 E : &fixups));
198 E : EXPECT_TRUE(fixups.empty());
199 :
200 E : EXPECT_EQ(kSearchSucceeded,
201 : FindAndLoadDiaDebugStreamByName(kFixupDiaDebugStreamName,
202 : dia_session.get(),
203 E : &fixups));
204 E : EXPECT_FALSE(fixups.empty());
205 E : }
206 :
207 : class DiaUtilVisitorTest : public DiaUtilTest {
208 : public:
209 E : void SetUp() override {
210 E : ASSERT_TRUE(CreateDiaSource(dia_source_.Receive()));
211 E : ASSERT_TRUE(CreateDiaSession(
212 : testing::GetExeRelativePath(testing::kTestDllName),
213 : dia_source_.get(),
214 : dia_session_.Receive()));
215 E : ASSERT_EQ(S_OK, dia_session_->get_globalScope(dia_globals_.Receive()));
216 E : }
217 :
218 E : bool OnFunction(StringVector* names, IDiaSymbol* function) {
219 E : EXPECT_TRUE(IsSymTag(function, SymTagFunction));
220 E : ScopedBstr name;
221 E : EXPECT_EQ(S_OK, function->get_name(name.Receive()));
222 E : names->push_back(common::ToString(name));
223 E : return true;
224 E : }
225 :
226 E : bool OnCompiland(StringVector* names, IDiaSymbol* compiland) {
227 E : EXPECT_TRUE(IsSymTag(compiland, SymTagCompiland));
228 E : ScopedBstr name;
229 E : EXPECT_EQ(S_OK, compiland->get_name(name.Receive()));
230 E : names->push_back(common::ToString(name));
231 E : return true;
232 E : }
233 :
234 : bool OnCompilandFind(const std::wstring& compiland_path,
235 : ScopedComPtr<IDiaSymbol>* compiland_out,
236 E : IDiaSymbol* compiland) {
237 E : EXPECT_TRUE(IsSymTag(compiland, SymTagCompiland));
238 E : ScopedBstr name;
239 E : EXPECT_EQ(S_OK, compiland->get_name(name.Receive()));
240 E : if (testing::Value(common::ToString(name), IsSameFile(compiland_path))) {
241 E : *compiland_out = compiland;
242 E : return false;
243 : }
244 i : return true;
245 E : }
246 :
247 : typedef std::set<std::pair<DWORD, DWORD>> LineSet;
248 : typedef std::map<std::wstring, LineSet, FilePathLess> LineMap;
249 E : bool OnLine(LineMap* line_map, IDiaLineNumber* line) {
250 E : DCHECK_NE(reinterpret_cast<LineMap*>(NULL), line_map);
251 E : DCHECK_NE(reinterpret_cast<IDiaLineNumber*>(NULL), line);
252 :
253 E : ScopedComPtr<IDiaSourceFile> source_file;
254 E : EXPECT_HRESULT_SUCCEEDED(line->get_sourceFile(source_file.Receive()));
255 :
256 E : ScopedBstr source_name;
257 E : EXPECT_HRESULT_SUCCEEDED(source_file->get_fileName(source_name.Receive()));
258 :
259 E : ScopedComPtr<IDiaSymbol> compiland;
260 E : EXPECT_HRESULT_SUCCEEDED(line->get_compiland(compiland.Receive()));
261 :
262 E : DWORD line_number = 0;
263 E : EXPECT_HRESULT_SUCCEEDED(line->get_lineNumber(&line_number));
264 E : DWORD line_number_end = 0;
265 E : EXPECT_HRESULT_SUCCEEDED(line->get_lineNumberEnd(&line_number_end));
266 :
267 : // This doesn't necessarily have to hold, but so far it seems to do so.
268 E : EXPECT_EQ(line_number, line_number_end);
269 :
270 E : (*line_map)[common::ToString(source_name)].insert(
271 : std::make_pair(line_number, line_number_end));
272 :
273 E : return true;
274 E : }
275 :
276 : ScopedComPtr<IDiaDataSource> dia_source_;
277 : ScopedComPtr<IDiaSession> dia_session_;
278 : ScopedComPtr<IDiaSymbol> dia_globals_;
279 : };
280 :
281 E : TEST_F(DiaUtilVisitorTest, ChildVisitorTest) {
282 E : ChildVisitor visitor(dia_globals_.get(), SymTagFunction);
283 :
284 E : StringVector function_names;
285 E : ASSERT_TRUE(visitor.VisitChildren(
286 : base::Bind(&DiaUtilVisitorTest::OnFunction,
287 : base::Unretained(this),
288 E : &function_names)));
289 :
290 : // Expect that we found a bunch of functions.
291 E : ASSERT_LT(1U, function_names.size());
292 : // One of them should be "DllMain".
293 E : ASSERT_THAT(function_names, testing::Contains(L"DllMain"));
294 E : }
295 :
296 E : TEST_F(DiaUtilVisitorTest, CompilandVisitorTest) {
297 E : CompilandVisitor visitor(dia_session_.get());
298 :
299 E : StringVector compiland_names;
300 E : ASSERT_TRUE(visitor.VisitAllCompilands(
301 : base::Bind(&DiaUtilVisitorTest::OnCompiland,
302 : base::Unretained(this),
303 E : &compiland_names)));
304 :
305 : // We expect to have seen some compiland_names.
306 E : ASSERT_LT(0U, compiland_names.size());
307 :
308 : // One of the compiland_names should be the test_dll.obj file.
309 E : std::string test_dll_path(kTestDllObjPath);
310 E : std::wstring test_dll_wide_path(test_dll_path.begin(), test_dll_path.end());
311 : base::FilePath test_dll_obj =
312 E : base::MakeAbsoluteFilePath(
313 : testing::GetOutputRelativePath(test_dll_wide_path.c_str()));
314 :
315 E : ASSERT_TRUE(StringVectorContainsPath(compiland_names, test_dll_obj.value()));
316 E : }
317 :
318 E : TEST_F(DiaUtilVisitorTest, LineVisitorTest) {
319 E : CompilandVisitor compiland_visitor(dia_session_.get());
320 :
321 : // Start by finding the test dll compiland.
322 E : ScopedComPtr<IDiaSymbol> compiland;
323 E : std::string test_dll_path(kTestDllObjPath);
324 E : std::wstring test_dll_wide_path(test_dll_path.begin(), test_dll_path.end());
325 : base::FilePath test_dll_obj =
326 E : base::MakeAbsoluteFilePath(
327 : testing::GetOutputRelativePath(test_dll_wide_path.c_str()));
328 E : ASSERT_FALSE(compiland_visitor.VisitAllCompilands(
329 : base::Bind(&DiaUtilVisitorTest::OnCompilandFind,
330 : base::Unretained(this),
331 : test_dll_obj.value(),
332 E : &compiland)));
333 :
334 E : ASSERT_TRUE(compiland != NULL);
335 :
336 : // Now enumerate all line entries in that compiland.
337 E : LineVisitor line_visitor(dia_session_.get(), compiland.get());
338 :
339 E : LineMap line_map;
340 E : ASSERT_TRUE(line_visitor.VisitLines(
341 : base::Bind(&DiaUtilVisitorTest::OnLine,
342 : base::Unretained(this),
343 E : &line_map)));
344 :
345 : // We expect to have at least one file.
346 E : ASSERT_LE(1U, line_map.size());
347 :
348 : // Expect test_dll.cc to be in the line map, and to contain at least one line
349 : // worth of information. Search for equivalent paths because under some
350 : // mysterious poorly understood conditions the drive letter in the PDB may or
351 : // may not be capitalized.
352 : base::FilePath test_dll_cc =
353 E : testing::GetSrcRelativePath(L"syzygy\\pe\\test_dll.cc");
354 E : LineMap::const_iterator it = line_map.begin();
355 E : for (; it != line_map.end(); ++it) {
356 E : if (PathsAreEquivalent(it->first, test_dll_cc.value()))
357 E : break;
358 i : }
359 E : ASSERT_TRUE(it != line_map.end());
360 E : ASSERT_LT(1U, it->second.size());
361 E : }
362 :
363 : } // namespace pe
|