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_writer.h"
16 :
17 : #include "base/logging.h"
18 : #include "syzygy/pdb/pdb_constants.h"
19 : #include "syzygy/pdb/pdb_data.h"
20 :
21 : namespace pdb {
22 :
23 : namespace {
24 :
25 : const uint32 kZeroBuffer[kPdbPageSize] = { 0 };
26 :
27 : // A byte-based bitmap for keeping track of free pages in an MSF/PDB file.
28 : // TODO(chrisha): Promote this to its own file and unittest it when we make
29 : // a library for MSF-specific stuff.
30 : class FreePageBitMap {
31 : public:
32 E : FreePageBitMap() : page_count_(0) {
33 E : }
34 :
35 E : void SetPageCount(uint32 page_count) {
36 E : page_count_ = page_count;
37 E : data_.resize((page_count + 7) / 8);
38 :
39 : // Double check our invariant.
40 E : DCHECK_LE(page_count, data_.size() * 8);
41 E : DCHECK_LE(page_count / 8, data_.size());
42 E : }
43 :
44 E : void SetBit(uint32 page_index, bool free) {
45 E : DCHECK_LT(page_index, page_count_);
46 :
47 E : uint32 byte = page_index / 8;
48 E : uint32 bit = page_index % 8;
49 E : uint8 bitmask = 1 << bit;
50 E : DCHECK_LT(byte, data_.size());
51 :
52 E : if (free) {
53 E : data_[byte] |= bitmask;
54 E : } else {
55 i : data_[byte] &= ~bitmask;
56 : }
57 E : }
58 :
59 E : void SetFree(uint32 page_index) { SetBit(page_index, true); }
60 : void SetUsed(uint32 page_index) { SetBit(page_index, false); }
61 :
62 : // TODO(chrisha): Make this an invariant of the class and move the logic
63 : // to SetPageCount. This involves both clearing and setting bits in that
64 : // case.
65 E : void Finalize() {
66 E : uint32 bits_left = data_.size() * 8 - page_count_;
67 E : DCHECK_LE(bits_left, 7u);
68 :
69 : // This leaves the top |bits_left| bits set.
70 E : uint8 bitmask = ~(0xFF >> bits_left);
71 :
72 : // Mark any bits as free beyond those specifically allocated.
73 E : data_.back() |= bitmask;
74 E : }
75 :
76 E : const std::vector<uint8>& data() const { return data_; }
77 :
78 : private:
79 : std::vector<uint8> data_;
80 : uint32 page_count_;
81 : };
82 :
83 : // A light-weight wrapper that allows a previously allocated buffer to be read
84 : // as a PdbStream.
85 : class ReadOnlyPdbStream : public PdbStream {
86 : public:
87 E : ReadOnlyPdbStream(const void* data, size_t bytes)
88 : : PdbStream(bytes), data_(data) {
89 E : }
90 :
91 : virtual bool ReadBytes(
92 E : void* dest, size_t count, size_t* bytes_read) OVERRIDE {
93 E : DCHECK(dest != NULL);
94 E : DCHECK(bytes_read != NULL);
95 :
96 E : bool result = true;
97 E : size_t bytes_to_read = count;
98 E : size_t bytes_left = length() - pos();
99 E : if (bytes_left < bytes_to_read) {
100 i : bytes_to_read = bytes_left;
101 i : result = false;
102 : }
103 :
104 : ::memcpy(dest, reinterpret_cast<const uint8*>(data_) + pos(),
105 E : bytes_to_read);
106 E : Seek(pos() + bytes_to_read);
107 :
108 E : *bytes_read = bytes_to_read;
109 E : return result;
110 E : }
111 :
112 : private:
113 : const void* data_;
114 : };
115 :
116 : // Appends a page to the provided file, adding the written page ID to the vector
117 : // of @p pages_written, and incrementing the total @P page_count. This will
118 : // occasionally cause more than one single page to be written to the output,
119 : // thus advancing @p page_count by more than one (when reserving pages for the
120 : // free page map). It is expected that @data be kPdbPageSize in length.
121 : // @pre the file is expected to be positioned at @p *page_count * kPdbPageSize
122 : // when entering this routine
123 : // @post the file will be positioned at @p *page_count * kPdbPageSiz when
124 : // exiting this routine.
125 : bool AppendPage(const void* data,
126 : std::vector<uint32>* pages_written,
127 : uint32* page_count,
128 E : FILE* file) {
129 E : DCHECK(data != NULL);
130 E : DCHECK(pages_written != NULL);
131 E : DCHECK(page_count != NULL);
132 E : DCHECK(file != NULL);
133 :
134 E : uint32 local_page_count = *page_count;
135 :
136 : // The file is written sequentially, so it will already be pointing to
137 : // the appropriate spot.
138 : DCHECK_EQ(local_page_count * kPdbPageSize,
139 E : static_cast<uint32>(::ftell(file)));
140 :
141 : // If we're due to allocate pages for the free page map, then do so.
142 E : if (((*page_count) % kPdbPageSize) == 1) {
143 : if (::fwrite(kZeroBuffer, 1, kPdbPageSize, file) != kPdbPageSize ||
144 E : ::fwrite(kZeroBuffer, 1, kPdbPageSize, file) != kPdbPageSize) {
145 i : LOG(ERROR) << "Failed to allocate free page map pages.";
146 i : return false;
147 : }
148 E : local_page_count += 2;
149 : }
150 :
151 : // Write the page itself.
152 E : if (::fwrite(data, 1, kPdbPageSize, file) != kPdbPageSize) {
153 i : LOG(ERROR) << "Failed to write page " << *page_count << ".";
154 i : return false;
155 : }
156 E : pages_written->push_back(local_page_count);
157 E : ++local_page_count;
158 :
159 : DCHECK_EQ(local_page_count * kPdbPageSize,
160 E : static_cast<uint32>(::ftell(file)));
161 :
162 E : *page_count = local_page_count;
163 E : return true;
164 E : }
165 :
166 E : bool WriteFreePageBitMap(const FreePageBitMap& free, FILE* file) {
167 E : DCHECK(file != NULL);
168 :
169 E : const uint8* data = free.data().data();
170 E : size_t bytes_left = free.data().size();
171 E : size_t page_index = 1;
172 E : size_t bytes_to_write = kPdbPageSize;
173 E : while (true) {
174 E : if (::fseek(file, page_index * kPdbPageSize, SEEK_SET) != 0) {
175 i : LOG(ERROR) << "Failed to seek to page " << page_index << ".";
176 i : return false;
177 : }
178 :
179 E : bytes_to_write = kPdbPageSize;
180 E : if (bytes_left < bytes_to_write)
181 E : bytes_to_write = bytes_left;
182 :
183 E : if (::fwrite(data, 1, bytes_to_write, file) != bytes_to_write) {
184 i : LOG(ERROR) << "Failed to write page " << page_index
185 : << " of free page map.";
186 i : return false;
187 : }
188 :
189 E : bytes_left -= bytes_to_write;
190 E : if (bytes_left == 0)
191 E : break;
192 :
193 E : data += bytes_to_write;
194 E : page_index += kPdbPageSize;
195 E : }
196 :
197 : // Was the last write partial? If so, we need to flush out the rest of the
198 : // free page map with ones (0xFF bytes).
199 E : if (bytes_to_write < kPdbPageSize) {
200 : // Create a vector of bytes with all the bits set.
201 E : std::vector<uint8> ones(kPdbPageSize - bytes_to_write, 0xFF);
202 E : if (::fwrite(ones.data(), 1, ones.size(), file) != ones.size()) {
203 i : LOG(ERROR) << "Failed to pad page " << page_index
204 : << " of free page map.";
205 i : return false;
206 : }
207 E : }
208 :
209 E : return true;
210 E : }
211 :
212 : } // namespace
213 :
214 E : PdbWriter::PdbWriter() {
215 E : }
216 :
217 E : PdbWriter::~PdbWriter() {
218 E : }
219 :
220 E : bool PdbWriter::Write(const base::FilePath& pdb_path, const PdbFile& pdb_file) {
221 E : file_.reset(file_util::OpenFile(pdb_path, "wb"));
222 E : if (!file_.get()) {
223 i : LOG(ERROR) << "Failed to create '" << pdb_path.value() << "'.";
224 i : return false;
225 : }
226 :
227 : // Initialize the directory with stream count and lengths.
228 E : std::vector<uint32> directory;
229 E : directory.push_back(pdb_file.StreamCount());
230 E : for (size_t i = 0; i < pdb_file.StreamCount(); ++i) {
231 : // Null streams have an implicit zero length.
232 E : PdbStream* stream = pdb_file.GetStream(i);
233 E : if (stream == NULL)
234 E : directory.push_back(0);
235 E : else
236 E : directory.push_back(stream->length());
237 E : }
238 :
239 : // Reserve space for the header page, the two free page map pages, and a
240 : // fourth empty page. The fourth empty page doesn't appear to be strictly
241 : // necessary but MSF/PDB files produced by MS tools always contain it.
242 E : uint32 page_count = 4;
243 E : for (uint32 i = 0; i < page_count; ++i) {
244 E : if (::fwrite(kZeroBuffer, 1, kPdbPageSize, file_.get()) != kPdbPageSize) {
245 i : LOG(ERROR) << "Failed to allocate preamble page.";
246 i : return false;
247 : }
248 E : }
249 :
250 : // Append all the streams after the preamble and build the directory while
251 : // we're at it. We keep track of which pages host stream 0 for some free page
252 : // map bookkeeping later on.
253 E : size_t stream0_start = directory.size();
254 E : size_t stream0_end = 0;
255 E : for (size_t i = 0; i < pdb_file.StreamCount(); ++i) {
256 E : if (i == 1)
257 E : stream0_end = directory.size();
258 :
259 : // Null streams are treated as empty streams.
260 E : PdbStream* stream = pdb_file.GetStream(i);
261 E : if (stream == NULL || stream->length() == 0)
262 E : continue;
263 :
264 : // Write the stream, updating the directory and page index. This routine
265 : // takes care of making room for the free page map pages.
266 E : if (!AppendStream(stream, &directory, &page_count)) {
267 i : LOG(ERROR) << "Failed to write stream " << i << ".";
268 i : return false;
269 : }
270 E : }
271 E : DCHECK_LE(stream0_start, stream0_end);
272 :
273 : // Write the directory, and keep track of the pages it is written to.
274 E : std::vector<uint32> directory_pages;
275 : scoped_refptr<PdbStream> directory_stream(new ReadOnlyPdbStream(
276 E : directory.data(), sizeof(directory[0]) * directory.size()));
277 E : if (!AppendStream(directory_stream.get(), &directory_pages, &page_count)) {
278 i : LOG(ERROR) << "Failed to write directory.";
279 i : return false;
280 : }
281 :
282 : // Write the root directory, and keep track of the pages it is written to.
283 : // These will in turn go into the header root directory pointers.
284 E : std::vector<uint32> root_directory_pages;
285 : scoped_refptr<PdbStream> root_directory_stream(new ReadOnlyPdbStream(
286 : directory_pages.data(),
287 E : sizeof(directory_pages[0]) * directory_pages.size()));
288 : if (!AppendStream(root_directory_stream.get(), &root_directory_pages,
289 E : &page_count)) {
290 i : LOG(ERROR) << "Failed to write root directory.";
291 i : return false;
292 : }
293 :
294 : // Write the header.
295 : if (!WriteHeader(root_directory_pages,
296 : sizeof(directory[0]) * directory.size(),
297 E : page_count)) {
298 i : LOG(ERROR) << "Failed to write PDB header.";
299 i : return false;
300 : }
301 :
302 : // Initialize the free page bit map. The pages corresponding to stream 0 are
303 : // always marked as free, as well as page 3 which we allocated in the
304 : // preamble.
305 E : FreePageBitMap free;
306 E : free.SetPageCount(page_count);
307 E : free.SetFree(3);
308 E : for (size_t i = stream0_start; i < stream0_end; ++i)
309 E : free.SetFree(directory[i]);
310 E : free.Finalize();
311 :
312 E : if (!WriteFreePageBitMap(free, file_.get())) {
313 i : LOG(ERROR) << "Failed to write free page bitmap.";
314 i : return false;
315 : }
316 :
317 : // On success we want the file to be closed right away.
318 E : file_.reset();
319 :
320 E : return true;
321 E : }
322 :
323 : bool PdbWriter::AppendStream(PdbStream* stream,
324 : std::vector<uint32>* pages_written,
325 E : uint32* page_count) {
326 E : DCHECK(stream != NULL);
327 E : DCHECK(pages_written != NULL);
328 E : DCHECK(page_count != NULL);
329 :
330 : #ifndef NDEBUG
331 E : size_t old_pages_written_count = pages_written->size();
332 E : size_t old_page_count = *page_count;
333 : #endif
334 :
335 : // Write the stream page by page.
336 E : stream->Seek(0);
337 E : uint8 buffer[kPdbPageSize] = { 0 };
338 E : size_t bytes_left = stream->length();
339 E : while (bytes_left) {
340 E : size_t bytes_to_read = sizeof(buffer);
341 E : if (bytes_to_read > bytes_left) {
342 E : bytes_to_read = bytes_left;
343 :
344 : // If we're only reading a partial buffer then pad the end of it with
345 : // zeros.
346 E : ::memset(buffer + bytes_to_read, 0, sizeof(buffer) - bytes_to_read);
347 : }
348 :
349 : // Read the buffer from the stream.
350 E : size_t bytes_read = 0;
351 : if (!stream->ReadBytes(buffer, bytes_to_read, &bytes_read) ||
352 E : bytes_read != bytes_to_read) {
353 i : size_t offset = stream->length() - bytes_left;
354 i : LOG(ERROR) << "Failed to read " << bytes_to_read << " bytes at offset "
355 : << offset << " of PDB stream.";
356 i : return false;
357 : }
358 :
359 E : if (!AppendPage(buffer, pages_written, page_count, file_.get()))
360 i : return false;
361 :
362 E : bytes_left -= bytes_read;
363 E : }
364 E : DCHECK_EQ(0u, bytes_left);
365 :
366 : #ifndef NDEBUG
367 : size_t expected_pages_written = (stream->length() + kPdbPageSize - 1) /
368 E : kPdbPageSize;
369 : DCHECK_EQ(old_pages_written_count + expected_pages_written,
370 E : pages_written->size());
371 : // We can't say anything about |page_count| as AppendPage occasionally snags
372 : // extra pages for the free page map.
373 : #endif
374 :
375 E : return true;
376 E : }
377 :
378 : bool PdbWriter::WriteHeader(const std::vector<uint32>& root_directory_pages,
379 : size_t directory_size,
380 E : uint32 page_count) {
381 E : VLOG(1) << "Writing MSF Header ...";
382 :
383 E : PdbHeader header = { 0 };
384 :
385 : // Make sure the root directory pointers won't overflow.
386 E : if (root_directory_pages.size() > arraysize(header.root_pages)) {
387 E : LOG(ERROR) << "Too many root directory pages for header ("
388 : << root_directory_pages.size() << " > "
389 : << arraysize(header.root_pages) << ").";
390 E : return false;
391 : }
392 :
393 : // Seek to the beginning of the file so we can stamp in the header.
394 E : if (::fseek(file_.get(), 0, SEEK_SET) != 0) {
395 i : LOG(ERROR) << "Seek failed while writing header.";
396 i : return false;
397 : }
398 :
399 : ::memcpy(header.magic_string, kPdbHeaderMagicString,
400 E : sizeof(kPdbHeaderMagicString));
401 E : header.page_size = kPdbPageSize;
402 E : header.free_page_map = 1;
403 E : header.num_pages = page_count;
404 E : header.directory_size = directory_size;
405 E : header.reserved = 0;
406 : ::memcpy(header.root_pages,
407 : root_directory_pages.data(),
408 E : sizeof(root_directory_pages[0]) * root_directory_pages.size());
409 :
410 E : if (::fwrite(&header, sizeof(header), 1, file_.get()) != 1) {
411 i : LOG(ERROR) << "Failed to write header.";
412 i : return false;
413 : }
414 :
415 E : return true;
416 E : }
417 :
418 : } // namespace pdb
|