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