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