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_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 : } // namespace
28 :
29 E : PdbWriter::PdbWriter() {
30 E : }
31 :
32 E : PdbWriter::~PdbWriter() {
33 E : }
34 :
35 E : bool PdbWriter::Write(const FilePath& pdb_path, const PdbFile& pdb_file) {
36 E : file_.reset(file_util::OpenFile(pdb_path, "wb"));
37 E : if (!file_.get()) {
38 i : LOG(ERROR) << "Failed to create '" << pdb_path.value() << "'.";
39 i : return false;
40 : }
41 :
42 E : uint32 total_bytes = 0;
43 :
44 : // Reserve space for the header and free page map.
45 : // TODO(rogerm): The free page map is a kludge. This should be sized to
46 : // correspond to the file instead of just one page. It should be relocated
47 : // to the end and sized properly.
48 E : if (fseek(file_.get(), kPdbPageSize * 3, SEEK_SET) != 0) {
49 i : LOG(ERROR) << "Failed to reserve header and free page map.";
50 i : return false;
51 : }
52 E : total_bytes += kPdbPageSize * 3;
53 :
54 : // Append all the streams after the header.
55 E : StreamInfoList stream_info_list;
56 E : for (size_t i = 0; i < pdb_file.StreamCount(); ++i) {
57 E : PdbStream* stream = pdb_file.GetStream(i);
58 E : if (stream == NULL) {
59 : // A null stream is treated as an empty one.
60 E : StreamInfo info = { total_bytes, 0 };
61 E : stream_info_list.push_back(info);
62 E : } else {
63 : // Save the offset and length for later.
64 E : StreamInfo info = { total_bytes, stream->length() };
65 E : stream_info_list.push_back(info);
66 :
67 E : uint32 bytes_written = 0;
68 E : if (!AppendStream(stream, &bytes_written))
69 i : return false;
70 :
71 E : total_bytes += bytes_written;
72 E : DCHECK_EQ(0U, total_bytes % kPdbPageSize);
73 : }
74 E : }
75 :
76 : // Map out the directory: i.e., pages on which the streams have been written.
77 E : uint32 dir_size = 0;
78 E : uint32 dir_page = total_bytes / kPdbPageSize;
79 E : uint32 bytes_written = 0;
80 E : if (!WriteDirectory(stream_info_list, &dir_size, &bytes_written))
81 i : return false;
82 E : total_bytes += bytes_written;
83 :
84 : // Map out the directory roots: i.e., pages on which the directory has been
85 : // written.
86 E : uint32 dir_root_size = 0;
87 E : uint32 dir_root_page = (total_bytes / kPdbPageSize);
88 E : if (!WriteDirectoryPages(dir_size, dir_page, &dir_root_size, &bytes_written))
89 i : return false;
90 E : total_bytes += bytes_written;
91 :
92 : // Fill in the MSF header.
93 E : if (!WriteHeader(total_bytes, dir_size, dir_root_size, dir_root_page))
94 i : return false;
95 :
96 : // On success we want the file to be closed right away.
97 E : file_.reset();
98 E : return true;
99 E : }
100 :
101 : // Write an unsigned 32 bit value to the output file.
102 : bool PdbWriter::WriteUint32(const char* func,
103 : const char* desc,
104 E : uint32 value) {
105 E : DCHECK(func != NULL);
106 E : DCHECK(desc != NULL);
107 :
108 E : if (fwrite(&value, sizeof(value), 1, file_.get()) != 1) {
109 i : LOG(ERROR) << func << ": Error writing " << desc;
110 i : return false;
111 : }
112 :
113 E : return true;
114 E : }
115 :
116 : bool PdbWriter::PadToPageBoundary(const char* func,
117 : uint32 offset,
118 E : uint32* padding) {
119 E : DCHECK(padding != NULL);
120 :
121 E : *padding = (kPdbPageSize - (offset % kPdbPageSize)) % kPdbPageSize;
122 E : if (fwrite(kZeroBuffer, 1, *padding, file_.get()) != *padding) {
123 i : LOG(ERROR) << func << ": Error padding page";
124 i : return false;
125 : }
126 :
127 E : return true;
128 E : }
129 :
130 E : bool PdbWriter::AppendStream(PdbStream* stream, uint32* bytes_written) {
131 E : DCHECK(bytes_written != NULL);
132 :
133 : // Append the contents of source to output file.
134 E : stream->Seek(0);
135 : uint8 buffer[1 << 16];
136 E : while (true) {
137 E : size_t bytes_read = 0;
138 E : if (!stream->ReadBytes(buffer, sizeof(buffer), &bytes_read)) {
139 i : LOG(ERROR) << "Error reading from pdb stream";
140 i : return false;
141 E : } else if (bytes_read == 0) {
142 E : break;
143 : }
144 :
145 E : if (fwrite(buffer, 1, bytes_read, file_.get()) != bytes_read) {
146 i : LOG(ERROR) << "Error appending pdb stream to file";
147 i : return false;
148 : }
149 E : }
150 :
151 : // Pad to the end of the current page boundary.
152 E : uint32 padding = 0;
153 E : if (!PadToPageBoundary("AppendStream", stream->length(), &padding))
154 i : return false;
155 :
156 E : *bytes_written = stream->length() + padding;
157 E : DCHECK_EQ(0U, (*bytes_written) % kPdbPageSize);
158 :
159 E : return true;
160 E : }
161 :
162 : bool PdbWriter::WriteDirectory(const StreamInfoList& stream_info_list,
163 : uint32* dir_size,
164 E : uint32* bytes_written) {
165 : static const char* func = "WriteDirectory";
166 :
167 E : VLOG(1) << "Writing directory ...";
168 E : DCHECK(dir_size != NULL);
169 E : DCHECK(bytes_written != NULL);
170 :
171 :
172 : // The directory format is:
173 : // num_streams (32-bit)
174 : // + stream_length (32-bit) for each stream in num_streams
175 : // + page_offset (32-bit) for each page in each stream in num_streams
176 :
177 E : uint32 byte_count = 0;
178 :
179 : // Write the number of streams.
180 E : if (!WriteUint32(func, "stream count", stream_info_list.size()))
181 i : return false;
182 E : byte_count += sizeof(uint32);
183 :
184 : // Write the length of each stream.
185 E : for (StreamInfoList::const_iterator iter = stream_info_list.begin();
186 E : iter != stream_info_list.end(); ++iter) {
187 E : if (!WriteUint32(func, "stream length", iter->length))
188 i : return false;
189 E : byte_count += sizeof(uint32);
190 E : }
191 :
192 : // Write the page numbers for each page in each stream.
193 E : for (StreamInfoList::const_iterator iter = stream_info_list.begin();
194 E : iter != stream_info_list.end(); ++iter) {
195 E : DCHECK_EQ(0U, iter->offset % kPdbPageSize);
196 E : for (uint32 length = 0, page_number = iter->offset / kPdbPageSize;
197 E : length < iter->length;
198 E : length += kPdbPageSize, ++page_number) {
199 E : if (!WriteUint32(func, "page offset", page_number))
200 i : return false;
201 E : byte_count += sizeof(uint32);
202 E : }
203 E : }
204 :
205 : // Pad the directory to the next page boundary.
206 E : uint32 padding = 0;
207 E : if (!PadToPageBoundary(func, byte_count, &padding))
208 i : return false;
209 :
210 : // Return the output values
211 E : (*dir_size) = byte_count;
212 E : (*bytes_written) = byte_count + padding;
213 :
214 E : DCHECK_EQ(0U, (*bytes_written) % kPdbPageSize);
215 E : return true;
216 E : }
217 :
218 : bool PdbWriter::WriteDirectoryPages(uint32 dir_size,
219 : uint32 dir_page,
220 : uint32* dir_pages_size,
221 E : uint32* bytes_written) {
222 : static const char* func = "WriteDirectoryPages";
223 :
224 E : VLOG(1) << "Writing directory roots...";
225 E : DCHECK(dir_pages_size != NULL);
226 E : DCHECK(bytes_written != NULL);
227 :
228 : // Write all page offsets that are used in the directory.
229 E : uint32 byte_count = 0;
230 E : for (uint32 page_offset = 0; page_offset < dir_size;
231 E : page_offset += kPdbPageSize, ++dir_page) {
232 E : if (!WriteUint32(func, "page offset", dir_page))
233 i : return false;
234 E : byte_count += sizeof(uint32);
235 E : }
236 :
237 : // Pad to a page boundary.
238 E : uint32 padding = 0;
239 E : if (!PadToPageBoundary(func, byte_count, &padding))
240 i : return false;
241 :
242 E : (*dir_pages_size) = byte_count;
243 E : (*bytes_written) = byte_count + padding;
244 :
245 E : DCHECK_EQ(0U, (*bytes_written) % kPdbPageSize);
246 E : return true;
247 E : }
248 :
249 : bool PdbWriter::WriteHeader(uint32 file_size,
250 : uint32 dir_size,
251 : uint32 dir_root_size,
252 E : uint32 dir_root_page) {
253 E : VLOG(1) << "Writing MSF Header ...";
254 E : DCHECK_EQ(0U, file_size % kPdbPageSize);
255 :
256 : // Make sure the root pages list won't overflow.
257 E : if ((dir_root_size + kPdbPageSize - 1) / kPdbPageSize > kPdbMaxDirPages) {
258 i : LOG(ERROR) << "Too many directory root pages";
259 i : return false;
260 : }
261 :
262 E : if (fseek(file_.get(), 0, SEEK_SET) != 0) {
263 i : LOG(ERROR) << "Seek failed when writing header";
264 i : return false;
265 : }
266 :
267 E : PdbHeader header = { 0 };
268 : memcpy(header.magic_string, kPdbHeaderMagicString,
269 E : sizeof(kPdbHeaderMagicString));
270 E : header.page_size = kPdbPageSize;
271 E : header.free_page_map = 1;
272 E : header.num_pages = file_size / kPdbPageSize;
273 E : header.directory_size = dir_size;
274 E : header.reserved = 0;
275 :
276 E : for (uint32 page_offset = 0; page_offset < dir_root_size;
277 E : page_offset += kPdbPageSize, ++dir_root_page) {
278 E : header.root_pages[page_offset / kPdbPageSize] = dir_root_page;
279 E : }
280 :
281 E : if (fwrite(&header, sizeof(header), 1, file_.get()) != 1) {
282 i : LOG(ERROR) << "Failed writing header";
283 i : return false;
284 : }
285 :
286 E : return true;
287 E : }
288 :
289 : } // namespace pdb
|