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 : #include "syzygy/pdb/pdb_util.h"
16 :
17 : #include <objbase.h>
18 : #include <vector>
19 :
20 : #include "base/command_line.h"
21 : #include "base/path_service.h"
22 : #include "base/scoped_native_library.h"
23 : #include "base/files/scoped_temp_dir.h"
24 : #include "base/process/launch.h"
25 : #include "base/strings/utf_string_conversions.h"
26 : #include "base/win/pe_image.h"
27 : #include "gmock/gmock.h"
28 : #include "gtest/gtest.h"
29 : #include "syzygy/common/dbghelp_util.h"
30 : #include "syzygy/core/unittest_util.h"
31 : #include "syzygy/pdb/pdb_byte_stream.h"
32 : #include "syzygy/pdb/pdb_reader.h"
33 : #include "syzygy/pdb/pdb_stream_reader.h"
34 : #include "syzygy/pdb/pdb_writer.h"
35 : #include "syzygy/pdb/unittest_util.h"
36 : #include "syzygy/pe/pe_data.h"
37 : #include "syzygy/pe/unittest_util.h"
38 :
39 : namespace pdb {
40 :
41 : namespace {
42 :
43 : const wchar_t* kTempPdbFileName = L"temp.pdb";
44 : const wchar_t* kTempPdbFileName2 = L"temp2.pdb";
45 :
46 : const GUID kSampleGuid = {0xACDC900D, 0x9009, 0xFEED, {7, 6, 5, 4, 3, 2, 1, 0}};
47 :
48 : const PdbInfoHeader70 kSamplePdbHeader = {
49 : kPdbCurrentVersion,
50 : 1336402486, // 7 May 2012, 14:54:00 GMT.
51 : 999,
52 : {0xDEADBEEF, 0x900D, 0xF00D, {0, 1, 2, 3, 4, 5, 6, 7}}
53 : };
54 :
55 : const DbiHeader kSampleDbiHeader = {
56 : -1, // signature.
57 : 19990903, // version.
58 : 999, // age.
59 : };
60 :
61 : const OMAP kOmapToData[] = {
62 : {4096, 4096},
63 : {5012, 5012},
64 : {6064, 6064},
65 : {7048, 240504}
66 : };
67 :
68 : const OMAP kOmapFromData[] = {
69 : {4096, 4096},
70 : {5012, 5012},
71 : {240504, 7048}
72 : };
73 :
74 : class PdbUtilTest : public testing::Test {
75 : public:
76 E : PdbUtilTest() : process_(this) {
77 E : }
78 :
79 E : void SetUp() {
80 E : ASSERT_TRUE(common::SymInitialize(process_, NULL, false));
81 :
82 E : ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&new_guid_));
83 E : ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
84 :
85 E : temp_pdb_file_path_ = temp_dir_.path().Append(kTempPdbFileName);
86 E : temp_pdb_file_path2_ = temp_dir_.path().Append(kTempPdbFileName2);
87 E : }
88 :
89 E : void TearDown() {
90 E : ASSERT_TRUE(::SymCleanup(process_));
91 E : }
92 :
93 : void VerifyGuidData(const base::FilePath& pdb_path,
94 : const GUID& guid) {
95 : DWORD64 base_address =
96 : ::SymLoadModuleExW(process_,
97 : NULL,
98 : pdb_path.value().c_str(),
99 : NULL,
100 : 1,
101 : 1,
102 : NULL,
103 : 0);
104 : EXPECT_NE(0, base_address);
105 :
106 : // Get the module info to verify that the new PDB has the GUID we specified.
107 : IMAGEHLP_MODULEW64 module_info = { sizeof(module_info) };
108 : EXPECT_TRUE(::SymGetModuleInfoW64(process_, base_address, &module_info));
109 : EXPECT_EQ(new_guid_, module_info.PdbSig70);
110 :
111 : EXPECT_TRUE(::SymUnloadModule64(process_, base_address));
112 : }
113 :
114 : void VerifyOmapData(const base::FilePath& pdb_path,
115 : const std::vector<OMAP>& omap_to_list,
116 E : const std::vector<OMAP>& omap_from_list) {
117 : DWORD64 base_address =
118 E : ::SymLoadModuleExW(process_,
119 : NULL,
120 : pdb_path.value().c_str(),
121 : NULL,
122 : 1,
123 : 1,
124 : NULL,
125 : 0);
126 E : ASSERT_NE(0, base_address);
127 :
128 E : OMAP* omap_to = NULL;
129 E : DWORD64 omap_to_length = 0;
130 E : OMAP* omap_from = NULL;
131 E : DWORD64 omap_from_length = 0;
132 E : ASSERT_TRUE(::SymGetOmaps(process_,
133 : base_address,
134 : &omap_to,
135 : &omap_to_length,
136 : &omap_from,
137 : &omap_from_length));
138 :
139 E : ASSERT_EQ(omap_to_list.size(), omap_to_length);
140 E : EXPECT_EQ(0, memcmp(&omap_to_list[0], omap_to,
141 : omap_to_list.size() * sizeof(OMAP)));
142 E : ASSERT_EQ(omap_from_list.size(), omap_from_length);
143 E : EXPECT_EQ(0, memcmp(&omap_from_list[0], omap_from,
144 : omap_from_list.size() * sizeof(OMAP)));
145 :
146 E : EXPECT_TRUE(::SymUnloadModule64(process_, base_address));
147 E : }
148 :
149 : protected:
150 : HANDLE process_;
151 : GUID new_guid_;
152 : base::ScopedTempDir temp_dir_;
153 : base::FilePath temp_pdb_file_path_;
154 : base::FilePath temp_pdb_file_path2_;
155 : };
156 :
157 : class TestPdbStream : public PdbStream {
158 : public:
159 E : TestPdbStream() : PdbStream(0), bytes_(NULL) {
160 E : }
161 :
162 : template <typename T>
163 : explicit TestPdbStream(const T& t)
164 E : : PdbStream(sizeof(T)), bytes_(reinterpret_cast<const uint8_t*>(&t)) {}
165 :
166 : explicit TestPdbStream(const std::vector<char>& data)
167 : : PdbStream(data.size()),
168 : bytes_(reinterpret_cast<const uint8_t*>(&data.at(0))) {}
169 :
170 E : bool ReadBytesAt(size_t pos, size_t count, void* dest) override {
171 E : if (count > length() - pos)
172 i : return false;
173 E : ::memcpy(dest, bytes_ + pos, count);
174 E : return true;
175 E : }
176 :
177 : private:
178 : const uint8_t* const bytes_;
179 : };
180 :
181 : // Comparison operator for PdbInfoHeader70 objects.
182 : bool AreEqual(const PdbInfoHeader70& header1,
183 E : const PdbInfoHeader70& header2) {
184 E : return ::memcmp(&header1, &header2, sizeof(header1)) == 0;
185 E : }
186 :
187 : } // namespace
188 :
189 E : TEST(PdbBitSetTest, ReadEmptyStream) {
190 E : scoped_refptr<PdbStream> stream(new TestPdbStream());
191 E : pdb::PdbStreamReaderWithPosition reader(stream.get());
192 :
193 E : PdbBitSet bs;
194 E : EXPECT_FALSE(bs.Read(&reader));
195 E : }
196 :
197 E : TEST(PdbBitSetTest, SimpleMutators) {
198 E : PdbBitSet bs;
199 E : EXPECT_TRUE(bs.IsEmpty());
200 E : EXPECT_EQ(bs.size(), 0u);
201 E : bs.Resize(43);
202 E : EXPECT_EQ(bs.size(), 64u);
203 :
204 E : for (size_t i = 0; i < 64; ++i)
205 E : EXPECT_FALSE(bs.IsSet(i));
206 :
207 E : bs.Toggle(15);
208 E : EXPECT_TRUE(bs.IsSet(15));
209 E : bs.Toggle(15);
210 E : EXPECT_FALSE(bs.IsSet(15));
211 :
212 E : bs.Set(25);
213 E : EXPECT_TRUE(bs.IsSet(25));
214 E : bs.Clear(25);
215 E : EXPECT_FALSE(bs.IsSet(25));
216 :
217 E : for (size_t i = 0; i < 64; i += 10)
218 E : bs.Set(i);
219 :
220 E : for (size_t i = 0; i < 64; ++i)
221 E : EXPECT_EQ((i % 10) == 0, bs.IsSet(i));
222 E : }
223 :
224 E : TEST(PdbBitSetTest, ReadEmptyBitSet) {
225 E : const uint32_t kSize = 0;
226 E : scoped_refptr<PdbStream> stream(new TestPdbStream(kSize));
227 E : pdb::PdbStreamReaderWithPosition reader(stream.get());
228 E : PdbBitSet bs;
229 E : EXPECT_TRUE(bs.Read(&reader));
230 E : EXPECT_TRUE(bs.IsEmpty());
231 E : EXPECT_EQ(bs.size(), 0u);
232 E : }
233 :
234 E : TEST(PdbBitSetTest, ReadSingleDwordBitSet) {
235 E : const uint32_t kData[] = {1, (1 << 0) | (1 << 5) | (1 << 13)};
236 E : scoped_refptr<PdbStream> stream(new TestPdbStream(kData));
237 E : pdb::PdbStreamReaderWithPosition reader(stream.get());
238 E : PdbBitSet bs;
239 E : EXPECT_TRUE(bs.Read(&reader));
240 E : EXPECT_FALSE(bs.IsEmpty());
241 E : EXPECT_EQ(bs.size(), 32u);
242 E : for (size_t i = 0; i < bs.size(); ++i)
243 E : EXPECT_EQ(i == 0 || i == 5 || i == 13, bs.IsSet(i));
244 E : }
245 :
246 E : TEST(PdbBitSetTest, ReadMultiDwordBitSet) {
247 E : const uint32_t kData[] = {2, (1 << 0) | (1 << 5) | (1 << 13), (1 << 5)};
248 E : scoped_refptr<PdbStream> stream(new TestPdbStream(kData));
249 E : pdb::PdbStreamReaderWithPosition reader(stream.get());
250 E : PdbBitSet bs;
251 E : EXPECT_TRUE(bs.Read(&reader));
252 E : EXPECT_FALSE(bs.IsEmpty());
253 E : EXPECT_EQ(bs.size(), 64u);
254 E : for (size_t i = 0; i < bs.size(); ++i)
255 E : EXPECT_EQ(i == 0 || i == 5 || i == 13 || i == (32 + 5), bs.IsSet(i));
256 E : }
257 :
258 E : TEST(PdbBitSetTest, WriteEmptyBitSet) {
259 E : const uint32_t kData[] = {0};
260 E : scoped_refptr<PdbStream> stream(new TestPdbStream(kData));
261 E : pdb::PdbStreamReaderWithPosition reader(stream.get());
262 E : PdbBitSet bs;
263 E : EXPECT_TRUE(bs.Read(&reader));
264 :
265 E : scoped_refptr<PdbByteStream> new_stream(new PdbByteStream());
266 E : scoped_refptr<WritablePdbStream> writer(new_stream->GetWritableStream());
267 E : EXPECT_TRUE(bs.Write(writer.get(), true));
268 E : EXPECT_EQ(sizeof(kData), new_stream->length());
269 :
270 E : uint32_t data[arraysize(kData)] = {};
271 E : EXPECT_TRUE(new_stream->ReadBytesAt(0, sizeof(data), data));
272 E : EXPECT_THAT(data, testing::ElementsAreArray(kData));
273 E : }
274 :
275 E : TEST(PdbBitSetTest, WriteEmptyBitSetWithoutSize) {
276 E : const uint32_t kData[] = {0};
277 E : scoped_refptr<PdbStream> stream(new TestPdbStream(kData));
278 E : pdb::PdbStreamReaderWithPosition reader(stream.get());
279 E : PdbBitSet bs;
280 E : EXPECT_TRUE(bs.Read(&reader));
281 :
282 E : scoped_refptr<PdbByteStream> new_stream(new PdbByteStream());
283 E : scoped_refptr<WritablePdbStream> writer(new_stream->GetWritableStream());
284 E : EXPECT_TRUE(bs.Write(writer.get(), false));
285 :
286 E : EXPECT_EQ(0, new_stream->length());
287 E : }
288 :
289 E : TEST(PdbBitSetTest, WriteBitSet) {
290 E : const uint32_t kData[] = {2, (1 << 0) | (1 << 5) | (1 << 13), (1 << 5)};
291 E : scoped_refptr<PdbStream> stream(new TestPdbStream(kData));
292 E : pdb::PdbStreamReaderWithPosition reader(stream.get());
293 E : PdbBitSet bs;
294 E : EXPECT_TRUE(bs.Read(&reader));
295 :
296 E : scoped_refptr<PdbByteStream> new_stream(new PdbByteStream());
297 E : scoped_refptr<WritablePdbStream> writer(new_stream->GetWritableStream());
298 E : EXPECT_TRUE(bs.Write(writer.get(), true));
299 E : EXPECT_EQ(sizeof(kData), new_stream->length());
300 :
301 E : uint32_t data[arraysize(kData)] = {};
302 E : EXPECT_TRUE(new_stream->ReadBytesAt(0, sizeof(data), data));
303 E : EXPECT_THAT(data, testing::ElementsAreArray(kData));
304 E : }
305 :
306 E : TEST(PdbBitSetTest, WriteBitSetWithoutSize) {
307 E : const uint32_t kInputData[] = {2, (1 << 0) | (1 << 5) | (1 << 13), (1 << 5)};
308 E : const uint32_t kExpectedData[] = {(1 << 0) | (1 << 5) | (1 << 13), (1 << 5)};
309 E : scoped_refptr<PdbStream> stream(new TestPdbStream(kInputData));
310 E : pdb::PdbStreamReaderWithPosition reader(stream.get());
311 E : PdbBitSet bs;
312 E : EXPECT_TRUE(bs.Read(&reader));
313 :
314 E : scoped_refptr<PdbByteStream> new_stream(new PdbByteStream());
315 E : scoped_refptr<WritablePdbStream> writer(new_stream->GetWritableStream());
316 E : EXPECT_TRUE(bs.Write(writer.get(), false));
317 E : EXPECT_EQ(sizeof(kExpectedData), new_stream->length());
318 :
319 E : uint32_t data[arraysize(kExpectedData)] = {};
320 E : EXPECT_TRUE(new_stream->ReadBytesAt(0, sizeof(data), data));
321 E : EXPECT_THAT(data, testing::ElementsAreArray(kExpectedData));
322 E : }
323 :
324 E : TEST_F(PdbUtilTest, HashString) {
325 E : EXPECT_EQ(1024, HashString(""));
326 E : EXPECT_EQ(20527, HashString("___onexitend"));
327 E : EXPECT_EQ(24736, HashString("__imp____getmainargs"));
328 E : EXPECT_EQ(61647, HashString("___security_cookie"));
329 E : }
330 :
331 E : TEST_F(PdbUtilTest, GetDbiDbgHeaderOffsetTestDll) {
332 : // Test the test_dll.dll.pdb doesn't have Omap information.
333 E : PdbReader reader;
334 E : PdbFile pdb_file;
335 E : EXPECT_TRUE(reader.Read(
336 : testing::GetSrcRelativePath(testing::kTestPdbFilePath),
337 E : &pdb_file));
338 :
339 E : PdbStream* dbi_stream = pdb_file.GetStream(kDbiStream).get();
340 : DbiHeader dbi_header;
341 E : EXPECT_TRUE(dbi_stream->ReadBytesAt(0, sizeof(dbi_header), &dbi_header));
342 :
343 E : uint32_t offset = GetDbiDbgHeaderOffset(dbi_header);
344 E : EXPECT_LE(offset, dbi_stream->length() - sizeof(DbiDbgHeader));
345 :
346 : DbiDbgHeader dbi_dbg_header;
347 E : EXPECT_TRUE(
348 E : dbi_stream->ReadBytesAt(offset, sizeof(dbi_dbg_header), &dbi_dbg_header));
349 :
350 E : EXPECT_EQ(-1, dbi_dbg_header.omap_to_src);
351 E : EXPECT_EQ(-1, dbi_dbg_header.omap_from_src);
352 E : }
353 :
354 E : TEST_F(PdbUtilTest, GetDbiDbgHeaderOffsetOmappedTestDll) {
355 : // Test that omapped_test_dll.dll.pdb does have Omap information.
356 E : PdbReader reader;
357 E : PdbFile pdb_file;
358 E : EXPECT_TRUE(reader.Read(
359 : testing::GetSrcRelativePath(testing::kOmappedTestPdbFilePath),
360 E : &pdb_file));
361 :
362 E : PdbStream* dbi_stream = pdb_file.GetStream(kDbiStream).get();
363 : DbiHeader dbi_header;
364 E : EXPECT_TRUE(dbi_stream->ReadBytesAt(0, sizeof(dbi_header), &dbi_header));
365 :
366 E : uint32_t offset = GetDbiDbgHeaderOffset(dbi_header);
367 E : EXPECT_LE(offset, dbi_stream->length() - sizeof(DbiDbgHeader));
368 :
369 : DbiDbgHeader dbi_dbg_header;
370 E : EXPECT_TRUE(
371 E : dbi_stream->ReadBytesAt(offset, sizeof(dbi_dbg_header), &dbi_dbg_header));
372 :
373 E : EXPECT_NE(-1, dbi_dbg_header.omap_to_src);
374 E : EXPECT_NE(-1, dbi_dbg_header.omap_from_src);
375 E : }
376 :
377 E : TEST_F(PdbUtilTest, TestDllHasNoOmap) {
378 : // Test that test_dll.dll.pdb has no Omap information.
379 E : base::FilePath test_pdb_file_path = testing::GetSrcRelativePath(
380 : testing::kTestPdbFilePath);
381 : DWORD64 base_address =
382 E : ::SymLoadModuleExW(process_,
383 : NULL,
384 : test_pdb_file_path.value().c_str(),
385 : NULL,
386 : 1,
387 : 1,
388 : NULL,
389 : 0);
390 E : EXPECT_NE(0, base_address);
391 :
392 E : OMAP* omap_to = NULL;
393 E : DWORD64 omap_to_length = 0;
394 E : OMAP* omap_from = NULL;
395 E : DWORD64 omap_from_length = 0;
396 E : EXPECT_FALSE(::SymGetOmaps(process_,
397 : base_address,
398 : &omap_to,
399 : &omap_to_length,
400 : &omap_from,
401 E : &omap_from_length));
402 :
403 E : EXPECT_TRUE(::SymUnloadModule64(process_, base_address));
404 E : }
405 :
406 E : TEST_F(PdbUtilTest, SetOmapToAndFromStream) {
407 : // Add Omap information to test_dll.pdb and test that the output file
408 : // has Omap information.
409 E : std::vector<OMAP> omap_to_list(kOmapToData,
410 : kOmapToData + arraysize(kOmapToData));
411 E : std::vector<OMAP> omap_from_list(kOmapFromData,
412 : kOmapFromData + arraysize(kOmapFromData));
413 :
414 E : base::FilePath test_pdb_file_path = testing::GetSrcRelativePath(
415 : testing::kTestPdbFilePath);
416 E : PdbReader pdb_reader;
417 E : PdbFile pdb_file;
418 E : ASSERT_TRUE(pdb_reader.Read(test_pdb_file_path, &pdb_file));
419 :
420 E : EXPECT_TRUE(SetOmapToStream(omap_to_list, &pdb_file));
421 E : EXPECT_TRUE(SetOmapFromStream(omap_from_list, &pdb_file));
422 :
423 E : PdbWriter pdb_writer;
424 E : ASSERT_TRUE(pdb_writer.Write(temp_pdb_file_path_, pdb_file));
425 :
426 E : VerifyOmapData(temp_pdb_file_path_,
427 : omap_to_list,
428 : omap_from_list);
429 E : }
430 :
431 E : TEST_F(PdbUtilTest, PdbHeaderMatchesImageDebugDirectory) {
432 E : PdbReader reader;
433 E : PdbFile pdb_file;
434 E : EXPECT_TRUE(reader.Read(
435 : testing::GetSrcRelativePath(testing::kTestPdbFilePath),
436 E : &pdb_file));
437 :
438 E : PdbInfoHeader70 header = { 0 };
439 E : ASSERT_GE(pdb_file.StreamCount(), kPdbHeaderInfoStream);
440 E : ASSERT_TRUE(pdb_file.GetStream(kPdbHeaderInfoStream) != NULL);
441 E : EXPECT_TRUE(pdb_file.GetStream(kPdbHeaderInfoStream)
442 E : ->ReadBytesAt(0, sizeof(header), &header));
443 E : EXPECT_EQ(header.version, kPdbCurrentVersion);
444 :
445 E : base::NativeLibraryLoadError error;
446 : base::NativeLibrary test_dll =
447 E : base::LoadNativeLibrary(
448 : testing::GetSrcRelativePath(testing::kTestDllFilePath),
449 : &error);
450 E : ASSERT_TRUE(test_dll != NULL);
451 :
452 : // Make sure the DLL is unloaded on exit.
453 E : base::ScopedNativeLibrary test_dll_keeper(test_dll);
454 E : base::win::PEImage image(test_dll);
455 :
456 E : ASSERT_EQ(sizeof(IMAGE_DEBUG_DIRECTORY),
457 E : image.GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG));
458 : const IMAGE_DEBUG_DIRECTORY* debug_directory =
459 : reinterpret_cast<const IMAGE_DEBUG_DIRECTORY*>(
460 E : image.GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG));
461 :
462 E : ASSERT_EQ(IMAGE_DEBUG_TYPE_CODEVIEW, debug_directory->Type);
463 E : ASSERT_GE(debug_directory->SizeOfData, sizeof(pe::CvInfoPdb70));
464 :
465 : const pe::CvInfoPdb70* cv_info =
466 : reinterpret_cast<const pe::CvInfoPdb70*>(
467 E : image.RVAToAddr(debug_directory->AddressOfRawData));
468 :
469 E : ASSERT_EQ(pe::kPdb70Signature, cv_info->cv_signature);
470 E : ASSERT_EQ(header.signature, cv_info->signature);
471 E : ASSERT_EQ(header.pdb_age, cv_info->pdb_age);
472 E : }
473 :
474 E : TEST_F(PdbUtilTest, ReadPdbHeader) {
475 E : const base::FilePath pdb_path = testing::GetSrcRelativePath(
476 : testing::kTestPdbFilePath);
477 E : PdbInfoHeader70 pdb_header = {};
478 E : EXPECT_TRUE(ReadPdbHeader(pdb_path, &pdb_header));
479 E : }
480 :
481 E : TEST(EnsureStreamWritableTest, DoesNothingWhenAlreadyWritable) {
482 E : PdbFile pdb_file;
483 E : scoped_refptr<PdbStream> stream = new PdbByteStream();
484 E : size_t index = pdb_file.AppendStream(stream.get());
485 E : EXPECT_TRUE(EnsureStreamWritable(index, &pdb_file));
486 E : scoped_refptr<PdbStream> stream2 = pdb_file.GetStream(index);
487 E : EXPECT_EQ(stream.get(), stream2.get());
488 E : }
489 :
490 E : TEST(EnsureStreamWritableTest, WorksWhenReadOnly) {
491 E : PdbFile pdb_file;
492 E : scoped_refptr<PdbStream> stream = new TestPdbStream();
493 E : size_t index = pdb_file.AppendStream(stream.get());
494 E : EXPECT_TRUE(EnsureStreamWritable(index, &pdb_file));
495 E : scoped_refptr<PdbStream> stream2 = pdb_file.GetStream(index);
496 E : EXPECT_TRUE(stream2.get() != NULL);
497 E : EXPECT_NE(stream.get(), stream2.get());
498 E : EXPECT_TRUE(stream2->GetWritableStream() != NULL);
499 E : }
500 :
501 E : TEST(EnsureStreamWritableTest, FailsWhenNonExistent) {
502 E : PdbFile pdb_file;
503 E : EXPECT_FALSE(EnsureStreamWritable(45, &pdb_file));
504 E : }
505 :
506 E : TEST(SetGuidTest, FailsWhenStreamsDoNotExist) {
507 E : PdbFile pdb_file;
508 :
509 : // Leave the Pdb header missing.
510 E : pdb_file.SetStream(kPdbHeaderInfoStream, NULL);
511 E : pdb_file.SetStream(kDbiStream, new TestPdbStream(kSampleDbiHeader));
512 E : EXPECT_FALSE(SetGuid(kSampleGuid, &pdb_file));
513 :
514 : // Add the header stream, but leave the Dbi header missing.
515 E : pdb_file.SetStream(kPdbHeaderInfoStream, new TestPdbStream(kSamplePdbHeader));
516 E : pdb_file.SetStream(kDbiStream, NULL);
517 E : EXPECT_FALSE(SetGuid(kSampleGuid, &pdb_file));
518 E : }
519 :
520 E : TEST(SetGuidTest, FailsWhenStreamsAreTooShort) {
521 E : PdbFile pdb_file;
522 :
523 E : const uint8_t kByte = 6;
524 E : pdb_file.SetStream(kPdbHeaderInfoStream, new TestPdbStream(kByte));
525 E : pdb_file.SetStream(kDbiStream, new TestPdbStream(kSampleDbiHeader));
526 E : EXPECT_FALSE(SetGuid(kSampleGuid, &pdb_file));
527 :
528 E : pdb_file.SetStream(kPdbHeaderInfoStream, new TestPdbStream(kSamplePdbHeader));
529 E : pdb_file.SetStream(kDbiStream, new TestPdbStream(kByte));
530 E : EXPECT_FALSE(SetGuid(kSampleGuid, &pdb_file));
531 E : }
532 :
533 E : TEST(SetGuidTest, Succeeds) {
534 E : PdbFile pdb_file;
535 :
536 E : pdb_file.SetStream(kPdbHeaderInfoStream, new TestPdbStream(kSamplePdbHeader));
537 E : pdb_file.SetStream(kDbiStream, new TestPdbStream(kSampleDbiHeader));
538 :
539 : scoped_refptr<PdbStream> stream =
540 E : pdb_file.GetStream(kPdbHeaderInfoStream);
541 E : ASSERT_TRUE(stream.get() != NULL);
542 E : ASSERT_EQ(stream->length(), sizeof(PdbInfoHeader70));
543 :
544 E : uint32_t time1 = static_cast<uint32_t>(time(NULL));
545 E : EXPECT_TRUE(SetGuid(kSampleGuid, &pdb_file));
546 E : uint32_t time2 = static_cast<uint32_t>(time(NULL));
547 :
548 : // Read the new header.
549 E : PdbInfoHeader70 pdb_header = {};
550 E : stream = pdb_file.GetStream(kPdbHeaderInfoStream);
551 E : EXPECT_TRUE(stream->ReadBytesAt(0, sizeof(pdb_header), &pdb_header));
552 :
553 : // Validate that the fields are as expected.
554 E : EXPECT_LE(time1, pdb_header.timestamp);
555 E : EXPECT_LE(pdb_header.timestamp, time2);
556 E : EXPECT_EQ(1u, pdb_header.pdb_age);
557 E : EXPECT_EQ(kSampleGuid, pdb_header.signature);
558 :
559 E : DbiHeader dbi_header = {};
560 E : stream = pdb_file.GetStream(kDbiStream);
561 E : ASSERT_TRUE(stream.get() != NULL);
562 E : ASSERT_EQ(stream->length(), sizeof(dbi_header));
563 :
564 E : EXPECT_TRUE(stream->ReadBytesAt(0, sizeof(dbi_header), &dbi_header));
565 E : EXPECT_EQ(1u, dbi_header.age);
566 E : }
567 :
568 E : TEST(ReadHeaderInfoStreamTest, ReadFromPdbFile) {
569 E : const base::FilePath pdb_path = testing::GetSrcRelativePath(
570 : testing::kTestPdbFilePath);
571 :
572 E : PdbFile pdb_file;
573 E : PdbReader pdb_reader;
574 E : ASSERT_TRUE(pdb_reader.Read(pdb_path, &pdb_file));
575 :
576 E : PdbInfoHeader70 pdb_header = {};
577 E : NameStreamMap name_stream_map;
578 E : EXPECT_TRUE(ReadHeaderInfoStream(pdb_file, &pdb_header, &name_stream_map));
579 E : }
580 :
581 E : TEST(ReadHeaderInfoStreamTest, ReadEmptyStream) {
582 E : scoped_refptr<PdbStream> stream(new TestPdbStream());
583 E : PdbInfoHeader70 pdb_header = {};
584 E : NameStreamMap name_stream_map;
585 E : EXPECT_FALSE(
586 E : ReadHeaderInfoStream(stream.get(), &pdb_header, &name_stream_map));
587 E : }
588 :
589 E : TEST(ReadHeaderInfoStreamTest, ReadStreamWithOnlyHeader) {
590 E : scoped_refptr<PdbStream> reader(new PdbByteStream());
591 E : scoped_refptr<WritablePdbStream> writer(reader->GetWritableStream());
592 :
593 E : PdbInfoHeader70 pdb_header = {};
594 E : ASSERT_TRUE(writer->Write(pdb_header));
595 :
596 E : NameStreamMap name_stream_map;
597 E : EXPECT_FALSE(
598 E : ReadHeaderInfoStream(reader.get(), &pdb_header, &name_stream_map));
599 E : }
600 :
601 E : TEST(ReadHeaderInfoStreamTest, ReadStreamWithEmptyNameStreamMap) {
602 E : scoped_refptr<PdbStream> reader(new PdbByteStream());
603 E : scoped_refptr<WritablePdbStream> writer(reader->GetWritableStream());
604 :
605 E : PdbInfoHeader70 pdb_header = {};
606 E : ASSERT_TRUE(writer->Write(pdb_header));
607 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(0))); // total string length.
608 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(0))); // number of names.
609 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(0))); // size of bitsets.
610 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(0))); // first bitset.
611 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(0))); // second bitset.
612 :
613 E : NameStreamMap name_stream_map;
614 E : EXPECT_TRUE(
615 E : ReadHeaderInfoStream(reader.get(), &pdb_header, &name_stream_map));
616 E : EXPECT_EQ(name_stream_map.size(), 0u);
617 E : }
618 :
619 E : TEST(ReadHeaderInfoStreamTest, ReadStreamWithNameStreamMap) {
620 E : scoped_refptr<PdbStream> reader(new PdbByteStream());
621 E : scoped_refptr<WritablePdbStream> writer(reader->GetWritableStream());
622 :
623 E : PdbInfoHeader70 pdb_header = {};
624 E : ASSERT_TRUE(writer->Write(pdb_header));
625 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(9))); // total string length.
626 E : uint32_t offset1 = writer->pos();
627 E : ASSERT_TRUE(writer->Write(3, "/a")); // name 1.
628 E : uint32_t offset2 = writer->pos();
629 E : ASSERT_TRUE(writer->Write(3, "/b")); // name 2.
630 E : uint32_t offset3 = writer->pos();
631 E : ASSERT_TRUE(writer->Write(3, "/c")); // name 3.
632 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(3))); // number of names.
633 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(3))); // size of bitsets.
634 :
635 E : PdbBitSet present;
636 E : present.Resize(3);
637 E : present.Set(0);
638 E : present.Set(1);
639 E : present.Set(2);
640 E : ASSERT_TRUE(present.Write(writer.get(), true));
641 :
642 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(0))); // second bitset.
643 :
644 E : ASSERT_TRUE(writer->Write(0));
645 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(42)));
646 E : ASSERT_TRUE(writer->Write(offset2 - offset1));
647 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(7)));
648 E : ASSERT_TRUE(writer->Write(offset3 - offset1));
649 E : ASSERT_TRUE(writer->Write(static_cast<uint32_t>(95)));
650 :
651 E : NameStreamMap name_stream_map;
652 E : EXPECT_TRUE(
653 E : ReadHeaderInfoStream(reader.get(), &pdb_header, &name_stream_map));
654 :
655 E : NameStreamMap expected;
656 E : expected["/a"] = 42;
657 E : expected["/b"] = 7;
658 E : expected["/c"] = 95;
659 E : EXPECT_THAT(name_stream_map, testing::ContainerEq(expected));
660 E : }
661 :
662 E : TEST(ReadHeaderInfoStreamTest, ReadFromPdb) {
663 E : const base::FilePath pdb_path = testing::GetSrcRelativePath(
664 : testing::kTestPdbFilePath);
665 E : PdbFile pdb_file;
666 E : PdbReader pdb_reader;
667 E : EXPECT_TRUE(pdb_reader.Read(pdb_path, &pdb_file));
668 :
669 E : PdbInfoHeader70 pdb_header = {};
670 E : NameStreamMap name_stream_map;
671 E : EXPECT_TRUE(
672 : ReadHeaderInfoStream(pdb_file.GetStream(kPdbHeaderInfoStream).get(),
673 E : &pdb_header, &name_stream_map));
674 E : }
675 :
676 E : TEST(WriteHeaderInfoStreamTest, WriteToPdbFile) {
677 E : const base::FilePath pdb_path = testing::GetSrcRelativePath(
678 : testing::kTestPdbFilePath);
679 :
680 E : PdbFile pdb_file;
681 E : PdbReader pdb_reader;
682 E : ASSERT_TRUE(pdb_reader.Read(pdb_path, &pdb_file));
683 :
684 E : PdbInfoHeader70 pdb_header = {};
685 E : NameStreamMap name_stream_map;
686 E : ASSERT_TRUE(ReadHeaderInfoStream(pdb_file, &pdb_header, &name_stream_map));
687 :
688 E : pdb_header.pdb_age++;
689 E : name_stream_map["NewStream!"] = 999;
690 :
691 E : EXPECT_TRUE(WriteHeaderInfoStream(pdb_header, name_stream_map, &pdb_file));
692 :
693 E : PdbInfoHeader70 pdb_header2 = {};
694 E : NameStreamMap name_stream_map2;
695 E : ASSERT_TRUE(ReadHeaderInfoStream(pdb_file, &pdb_header2, &name_stream_map2));
696 :
697 E : EXPECT_TRUE(AreEqual(pdb_header, pdb_header2));
698 E : EXPECT_EQ(name_stream_map, name_stream_map2);
699 E : }
700 :
701 E : TEST(WriteHeaderInfoStreamTest, WriteEmpty) {
702 E : scoped_refptr<PdbStream> reader(new PdbByteStream());
703 E : scoped_refptr<WritablePdbStream> writer(reader->GetWritableStream());
704 :
705 E : NameStreamMap name_stream_map;
706 E : EXPECT_TRUE(WriteHeaderInfoStream(kSamplePdbHeader,
707 : name_stream_map,
708 E : writer.get()));
709 :
710 E : PdbInfoHeader70 read_pdb_header = {};
711 E : NameStreamMap read_name_stream_map;
712 E : EXPECT_TRUE(ReadHeaderInfoStream(reader.get(),
713 : &read_pdb_header,
714 E : &read_name_stream_map));
715 :
716 E : EXPECT_EQ(0, ::memcmp(&kSamplePdbHeader,
717 : &read_pdb_header,
718 E : sizeof(kSamplePdbHeader)));
719 E : EXPECT_THAT(name_stream_map, testing::ContainerEq(read_name_stream_map));
720 E : }
721 :
722 E : TEST(WriteHeaderInfoStreamTest, WriteNonEmpty) {
723 E : scoped_refptr<PdbStream> reader(new PdbByteStream());
724 E : scoped_refptr<WritablePdbStream> writer(reader->GetWritableStream());
725 :
726 E : NameStreamMap name_stream_map;
727 E : name_stream_map["/StreamFoo"] = 9;
728 E : name_stream_map["/StreamBar"] = 42;
729 E : name_stream_map["/Stream/With/A/Path"] = 19;
730 E : EXPECT_TRUE(WriteHeaderInfoStream(kSamplePdbHeader,
731 : name_stream_map,
732 E : writer.get()));
733 :
734 E : PdbInfoHeader70 read_pdb_header = {};
735 E : NameStreamMap read_name_stream_map;
736 E : EXPECT_TRUE(ReadHeaderInfoStream(reader.get(),
737 : &read_pdb_header,
738 E : &read_name_stream_map));
739 :
740 E : EXPECT_EQ(0, ::memcmp(&kSamplePdbHeader,
741 : &read_pdb_header,
742 E : sizeof(kSamplePdbHeader)));
743 E : EXPECT_THAT(name_stream_map, testing::ContainerEq(read_name_stream_map));
744 E : }
745 :
746 E : TEST_F(PdbUtilTest, NamedStreamsWorkWithPdbStr) {
747 : // We start by creating a PDB file (a copy of a checked in sample one) and
748 : // adding a new stream to it using our named-stream implementation.
749 : {
750 E : base::FilePath orig_pdb_path = testing::GetSrcRelativePath(
751 : testing::kTestPdbFilePath);
752 :
753 : // Read the sample PDB.
754 E : PdbReader pdb_reader;
755 E : PdbFile pdb_file;
756 E : ASSERT_TRUE(pdb_reader.Read(orig_pdb_path, &pdb_file));
757 :
758 : // Add a new stream to it.
759 E : scoped_refptr<PdbStream> foo_reader(new PdbByteStream());
760 E : scoped_refptr<WritablePdbStream> foo_writer(
761 : foo_reader->GetWritableStream());
762 E : size_t foo_index = pdb_file.AppendStream(foo_reader.get());
763 E : foo_writer->WriteString("foo");
764 :
765 : // Get the PDB header stream.
766 E : scoped_refptr<PdbStream> header_stream(pdb_file.GetStream(
767 : kPdbHeaderInfoStream));
768 E : ASSERT_TRUE(header_stream.get() != NULL);
769 :
770 : // Read the existing name-stream map.
771 E : PdbInfoHeader70 pdb_header = {};
772 E : NameStreamMap name_stream_map;
773 E : ASSERT_TRUE(ReadHeaderInfoStream(header_stream.get(), &pdb_header,
774 E : &name_stream_map));
775 :
776 : // Add an entry for the new stream.
777 E : name_stream_map["foo"] = foo_index;
778 :
779 : // Write the new header stream to it.
780 E : scoped_refptr<PdbStream> new_header_reader(new PdbByteStream());
781 E : scoped_refptr<WritablePdbStream> new_header_writer(
782 : new_header_reader->GetWritableStream());
783 E : ASSERT_TRUE(pdb::WriteHeaderInfoStream(pdb_header, name_stream_map,
784 E : new_header_writer.get()));
785 E : pdb_file.ReplaceStream(kPdbHeaderInfoStream, new_header_reader.get());
786 :
787 : // Write the PDB.
788 E : PdbWriter pdb_writer;
789 E : ASSERT_TRUE(pdb_writer.Write(temp_pdb_file_path_, pdb_file));
790 E : }
791 :
792 : // We've now created a new PDB file. We want to make sure that pdbstr.exe
793 : // plays nicely with our named streams by doing a few things:
794 : // (1) If we try to read a non-existing stream, we should get empty output.
795 : // (2) We should be able to read an existing stream and get non-empty output.
796 : // (3) We should be able to add a new stream, and then read it using our
797 : // mechanisms.
798 :
799 : // Get the path to pdbstr.exe, which we redistribute in third_party.
800 : base::FilePath pdbstr_path =
801 E : testing::GetSrcRelativePath(testing::kPdbStrPath);
802 :
803 : // Create the argument specifying the PDB path.
804 E : std::string pdb_arg = base::WideToUTF8(temp_pdb_file_path_.value());
805 E : pdb_arg.insert(0, "-p:");
806 :
807 : // First test: try to read a non-existing stream. Should produce no output.
808 : {
809 E : base::CommandLine cmd(pdbstr_path);
810 E : cmd.AppendArg(pdb_arg);
811 E : cmd.AppendArg("-r");
812 E : cmd.AppendArg("-s:nonexistent-stream-name");
813 E : base::LaunchOptions options;
814 E : options.inherit_handles = true;
815 E : base::Process process = base::LaunchProcess(cmd, options);
816 :
817 E : int exit_code = 0;
818 E : ASSERT_TRUE(process.WaitForExit(&exit_code));
819 E : ASSERT_EQ(-1, exit_code);
820 E : }
821 :
822 : // Second test: read an existing stream (the one we just added). Should
823 : // exit without error and return the expected contents (with a trailing
824 : // newline).
825 : {
826 E : base::CommandLine cmd(pdbstr_path);
827 E : cmd.AppendArg(pdb_arg);
828 E : cmd.AppendArg("-r");
829 E : cmd.AppendArg("-s:foo");
830 E : std::string output;
831 E : ASSERT_TRUE(base::GetAppOutput(cmd, &output));
832 E : ASSERT_EQ(std::string("foo\r\n"), output);
833 E : }
834 :
835 : // Third test: Add another new stream. This should return without error, and
836 : // we should then be able to read the stream using our mechanisms.
837 : {
838 E : base::FilePath bar_txt = temp_dir_.path().Append(L"bar.txt");
839 E : base::ScopedFILE bar_file(base::OpenFile(
840 : bar_txt, "wb"));
841 E : fprintf(bar_file.get(), "bar");
842 E : bar_file.reset();
843 :
844 E : std::string bar_arg = base::WideToUTF8(bar_txt.value());
845 E : bar_arg.insert(0, "-i:");
846 :
847 E : base::CommandLine cmd(pdbstr_path);
848 E : cmd.AppendArg(pdb_arg);
849 E : cmd.AppendArg("-w");
850 E : cmd.AppendArg("-s:bar");
851 E : cmd.AppendArg(bar_arg);
852 E : std::string output;
853 E : ASSERT_TRUE(base::GetAppOutput(cmd, &output));
854 E : ASSERT_TRUE(output.empty());
855 :
856 E : PdbFile pdb_file;
857 E : PdbReader pdb_reader;
858 E : ASSERT_TRUE(pdb_reader.Read(temp_pdb_file_path_, &pdb_file));
859 :
860 : // Get the PDB header stream.
861 E : scoped_refptr<PdbStream> header_stream(pdb_file.GetStream(
862 : kPdbHeaderInfoStream));
863 E : ASSERT_TRUE(header_stream.get() != NULL);
864 :
865 : // Read the existing name-stream map.
866 E : PdbInfoHeader70 pdb_header = {};
867 E : NameStreamMap name_stream_map;
868 E : ASSERT_TRUE(ReadHeaderInfoStream(header_stream.get(), &pdb_header,
869 E : &name_stream_map));
870 :
871 : // There should be a 'bar' stream.
872 E : ASSERT_TRUE(name_stream_map.count("bar"));
873 :
874 : // Get the bar stream.
875 E : scoped_refptr<PdbStream> bar_stream(pdb_file.GetStream(
876 : name_stream_map["bar"]));
877 E : ASSERT_TRUE(bar_stream.get() != NULL);
878 :
879 : // Read all of the data and ensure it is as expected.
880 E : std::string bar_data;
881 E : bar_data.resize(bar_stream->length());
882 E : ASSERT_TRUE(bar_stream->ReadBytesAt(0, bar_data.size(), &bar_data.at(0)));
883 E : ASSERT_EQ("bar", bar_data);
884 E : }
885 E : }
886 :
887 E : TEST_F(PdbUtilTest, LoadNamedStreamFromPdbFile) {
888 E : PdbReader reader;
889 E : PdbFile pdb_file;
890 E : EXPECT_TRUE(reader.Read(
891 : testing::GetOutputRelativePath(testing::kTestDllPdbName),
892 E : &pdb_file));
893 :
894 E : scoped_refptr<PdbStream> stream;
895 E : EXPECT_TRUE(LoadNamedStreamFromPdbFile(
896 E : "StreamThatDoesNotExist", &pdb_file, &stream));
897 E : EXPECT_TRUE(stream.get() == NULL);
898 :
899 : // The MSVC toolchain produces a handful of named streams whose existence we
900 : // can rely on.
901 E : EXPECT_TRUE(LoadNamedStreamFromPdbFile("/LinkInfo", &pdb_file, &stream));
902 E : ASSERT_TRUE(stream.get() != NULL);
903 E : }
904 :
905 : } // namespace pdb
|