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_reader.h"
16 :
17 : #include "base/logging.h"
18 : #include "base/string_util.h"
19 : #include "syzygy/pdb/pdb_file_stream.h"
20 :
21 : namespace pdb {
22 :
23 : namespace {
24 :
25 E : bool GetFileSize(FILE* file, uint32* size) {
26 E : DCHECK(file != NULL);
27 E : DCHECK(size != NULL);
28 :
29 E : if (fseek(file, 0, SEEK_END) != 0) {
30 i : LOG(ERROR) << "Failed seeking to end of file.";
31 i : return false;
32 : }
33 :
34 E : long temp = ftell(file);
35 E : if (temp == -1L) {
36 i : LOG(ERROR) << "Failed to read stream position.";
37 i : return false;
38 : }
39 E : DCHECK_GT(temp, 0);
40 :
41 E : (*size) = static_cast<uint32>(temp);
42 E : return true;
43 E : }
44 :
45 E : uint32 GetNumPages(const PdbHeader& header, uint32 num_bytes) {
46 E : return (num_bytes + header.page_size - 1) / header.page_size;
47 E : }
48 :
49 : } // namespace
50 :
51 E : bool PdbReader::Read(const base::FilePath& pdb_path, PdbFile* pdb_file) {
52 E : DCHECK(pdb_file != NULL);
53 :
54 E : pdb_file->Clear();
55 :
56 : scoped_refptr<RefCountedFILE> file(new RefCountedFILE(
57 E : file_util::OpenFile(pdb_path, "rb")));
58 E : if (!file->file()) {
59 E : LOG(ERROR) << "Unable to open '" << pdb_path.value() << "'.";
60 E : return false;
61 : }
62 :
63 : // Get the file size.
64 E : uint32 file_size = 0;
65 E : if (!GetFileSize(file->file(), &file_size)) {
66 i : LOG(ERROR) << "Unable to determine size of '" << pdb_path.value() << "'.";
67 i : return false;
68 : }
69 :
70 E : PdbHeader header = { 0 };
71 :
72 : // Read the header from the first page in the file.
73 E : uint32 header_page = 0;
74 : scoped_refptr<PdbFileStream> header_stream(new PdbFileStream(
75 E : file, sizeof(header), &header_page, kPdbPageSize));
76 E : if (!header_stream->Read(&header, 1)) {
77 i : LOG(ERROR) << "Failed to read PDB file header.";
78 i : return false;
79 : }
80 :
81 : // Sanity checks.
82 E : if (header.num_pages * header.page_size != file_size) {
83 E : LOG(ERROR) << "Invalid PDB file size.";
84 E : return false;
85 : }
86 :
87 : if (memcmp(header.magic_string, kPdbHeaderMagicString,
88 E : sizeof(kPdbHeaderMagicString)) != 0) {
89 i : LOG(ERROR) << "Invalid PDB magic string.";
90 i : return false;
91 : }
92 :
93 : // Load the directory page list (a sequence of uint32 page numbers that is
94 : // itself written across multiple root pages). To do this we need to know how
95 : // many pages are required to represent the directory, then we load a stream
96 : // containing that many page pointers from the root pages array.
97 : int num_dir_pages = static_cast<int>(GetNumPages(header,
98 E : header.directory_size));
99 : scoped_refptr<PdbFileStream> dir_page_stream(new PdbFileStream(
100 : file, num_dir_pages * sizeof(uint32),
101 E : header.root_pages, header.page_size));
102 E : scoped_ptr<uint32[]> dir_pages(new uint32[num_dir_pages]);
103 E : if (dir_pages.get() == NULL) {
104 i : LOG(ERROR) << "Failed to allocate directory pages.";
105 i : return false;
106 : }
107 E : if (!dir_page_stream->Read(dir_pages.get(), num_dir_pages)) {
108 i : LOG(ERROR) << "Failed to read directory page stream.";
109 i : return false;
110 : }
111 :
112 : // Load the actual directory.
113 E : int dir_size = static_cast<int>(header.directory_size / sizeof(uint32));
114 : scoped_refptr<PdbFileStream> dir_stream(new PdbFileStream(
115 E : file, header.directory_size, dir_pages.get(), header.page_size));
116 E : std::vector<uint32> directory(dir_size);
117 E : if (!dir_stream->Read(&directory[0], dir_size)) {
118 i : LOG(ERROR) << "Failed to read directory stream.";
119 i : return false;
120 : }
121 :
122 : // Iterate through the streams and construct PdbStreams.
123 E : const uint32& num_streams = directory[0];
124 E : const uint32* stream_lengths = &(directory[1]);
125 E : const uint32* stream_pages = &(directory[1 + num_streams]);
126 :
127 E : uint32 page_index = 0;
128 E : for (uint32 stream_index = 0; stream_index < num_streams; ++stream_index) {
129 : pdb_file->AppendStream(new PdbFileStream(file,
130 : stream_lengths[stream_index],
131 : stream_pages + page_index,
132 E : header.page_size));
133 E : page_index += GetNumPages(header, stream_lengths[stream_index]);
134 E : }
135 :
136 E : return true;
137 E : }
138 :
139 : } // namespace pdb
|