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