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 : #ifndef SYZYGY_PE_PE_FILE_IMPL_H_
16 : #define SYZYGY_PE_PE_FILE_IMPL_H_
17 :
18 : namespace pe {
19 :
20 : template <class ImageNtHeaders, DWORD MagicValidation>
21 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Init(
22 E : const base::FilePath& path) {
23 E : if (!PECoffFile::Init(path))
24 E : return false;
25 E : if (!ReadHeaders())
26 E : return false;
27 E : if (!ReadSections())
28 i : return false;
29 E : return true;
30 E : }
31 :
32 : template <class ImageNtHeaders, DWORD MagicValidation>
33 : void PEFileBase<ImageNtHeaders, MagicValidation>::GetSignature(
34 E : Signature* signature) const {
35 E : DCHECK_NE(static_cast<Signature*>(NULL), signature);
36 E : DCHECK_NE(static_cast<ImageNtHeaders*>(NULL), nt_headers_);
37 :
38 : // TODO(chrisha): Make GetSignature return a bool, and update all calling
39 : // sites.
40 E : base::FilePath abs_path(base::MakeAbsoluteFilePath(path_));
41 E : CHECK(!abs_path.empty());
42 :
43 E : signature->path = abs_path.value();
44 E : signature->base_address =
45 : AbsoluteAddress(nt_headers_->OptionalHeader.ImageBase);
46 E : signature->module_size = nt_headers_->OptionalHeader.SizeOfImage;
47 E : signature->module_time_date_stamp = nt_headers_->FileHeader.TimeDateStamp;
48 E : signature->module_checksum = nt_headers_->OptionalHeader.CheckSum;
49 E : }
50 :
51 : template <class ImageNtHeaders, DWORD MagicValidation>
52 : bool PEFileBase<ImageNtHeaders, MagicValidation>::DecodeRelocs(
53 E : RelocSet* relocs) const {
54 E : DCHECK_NE(static_cast<ImageNtHeaders*>(NULL), nt_headers_);
55 E : DCHECK_NE(static_cast<RelocSet*>(NULL), relocs);
56 :
57 : // Walk the relocs.
58 : IMAGE_DATA_DIRECTORY dir =
59 : nt_headers_->OptionalHeader.DataDirectory[
60 E : IMAGE_DIRECTORY_ENTRY_BASERELOC];
61 E : RelativeAddress offs(dir.VirtualAddress);
62 E : RelativeAddress end(offs + dir.Size);
63 :
64 E : const IMAGE_BASE_RELOCATION* hdr = NULL;
65 E : for (; offs < end; offs += hdr->SizeOfBlock) {
66 : // Read the next header.
67 E : if (!GetImageData(offs, sizeof(hdr), &hdr)) {
68 i : LOG(ERROR) << "Failed to read relocation block header.";
69 i : return false;
70 : }
71 :
72 : // Read the entries.
73 E : size_t num_relocs = (hdr->SizeOfBlock - sizeof(*hdr)) / sizeof(WORD);
74 E : const WORD* reloc_block = NULL;
75 E : if (!GetImageData(offs + sizeof(*hdr), sizeof(*reloc_block) * num_relocs,
76 : &reloc_block)) {
77 i : LOG(ERROR) << "Failed to read relocation entries.";
78 i : return false;
79 : }
80 :
81 : // Walk the entries.
82 E : for (size_t i = 0; i < num_relocs; ++i) {
83 E : uint8_t type = reloc_block[i] >> 12;
84 E : uint16_t offs = reloc_block[i] & 0xFFF;
85 E : DCHECK(type == IMAGE_REL_BASED_HIGHLOW ||
86 : type == IMAGE_REL_BASED_ABSOLUTE);
87 :
88 E : if (type == IMAGE_REL_BASED_HIGHLOW) {
89 : // Record the entry.
90 E : relocs->insert(RelativeAddress(hdr->VirtualAddress) + offs);
91 : }
92 E : }
93 E : }
94 :
95 E : DCHECK(offs == end);
96 E : return true;
97 E : }
98 :
99 : template <class ImageNtHeaders, DWORD MagicValidation>
100 : bool PEFileBase<ImageNtHeaders, MagicValidation>::ReadRelocs(
101 E : const RelocSet& relocs, RelocMap* reloc_values) const {
102 E : RelocSet::const_iterator it(relocs.begin());
103 E : for (; it != relocs.end(); ++it) {
104 E : const AbsoluteAddress* abs = NULL;
105 E : if (!GetImageData(*it, sizeof(*abs), &abs)) {
106 i : LOG(ERROR) << "Failed to read reloc at " << it->value() << ".";
107 i : return false;
108 : }
109 :
110 E : reloc_values->insert(std::make_pair(*it, *abs));
111 E : }
112 :
113 E : return true;
114 E : }
115 :
116 : template <class ImageNtHeaders, DWORD MagicValidation>
117 : bool PEFileBase<ImageNtHeaders, MagicValidation>::DecodeImports(
118 E : ImportDllVector* imports) const {
119 E : DCHECK_NE(static_cast<ImportDllVector*>(NULL), imports);
120 :
121 : // Walk the import thunks.
122 : IMAGE_DATA_DIRECTORY dir = nt_headers_->OptionalHeader.
123 E : DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
124 E : RelativeAddress offs(dir.VirtualAddress);
125 E : RelativeAddress end(offs + dir.Size);
126 :
127 E : const IMAGE_IMPORT_DESCRIPTOR* import_desc = NULL;
128 E : for (; offs < end; offs += sizeof(*import_desc)) {
129 E : if (!GetImageData(offs, sizeof(*import_desc), &import_desc)) {
130 i : LOG(ERROR) << "Unable to read import descriptor.";
131 i : return false;
132 : }
133 :
134 E : if (import_desc->Characteristics == 0 && import_desc->FirstThunk == 0) {
135 : // This is the last chunk, bail the loop.
136 E : break;
137 : }
138 :
139 E : std::string dll_name;
140 E : if (!ReadImageString(RelativeAddress(import_desc->Name), &dll_name)) {
141 i : LOG(ERROR) << "Unable to read import descriptor name.";
142 i : return false;
143 : }
144 :
145 : // Iterate the Import Name Table and the Import Address Table
146 : // concurrently. They will yield, respectively, the name of the
147 : // function and the address of the entry.
148 E : RelativeAddress int_offs(import_desc->OriginalFirstThunk);
149 E : RelativeAddress iat_offs(import_desc->FirstThunk);
150 :
151 E : imports->push_back(ImportDll());
152 E : ImportDll& dll = imports->back();
153 E : dll.name = dll_name;
154 E : dll.desc = *import_desc;
155 :
156 E : while (true) {
157 E : IMAGE_THUNK_DATA int_thunk = {};
158 E : IMAGE_THUNK_DATA iat_thunk = {};
159 :
160 E : if (!ReadImage(int_offs, &int_thunk, sizeof(int_thunk)) ||
161 : !ReadImage(iat_offs, &iat_thunk, sizeof(iat_thunk))) {
162 i : LOG(ERROR) << "Unable to read import name or address table thunk.";
163 i : return false;
164 : }
165 :
166 : // Are we at the end of the table?
167 E : if (int_thunk.u1.Function == 0) {
168 E : DCHECK_EQ(0U, iat_thunk.u1.Function);
169 E : break;
170 : }
171 :
172 E : uint16_t hint = 0;
173 E : uint16_t ordinal = 0;
174 E : std::string function_name;
175 E : if (int_thunk.u1.AddressOfData & IMAGE_ORDINAL_FLAG32) {
176 : // It's an ordinal.
177 E : ordinal = IMAGE_ORDINAL32(int_thunk.u1.Ordinal);
178 E : } else {
179 : // Read the hint word, followed by the function name.
180 E : RelativeAddress import_name(int_thunk.u1.AddressOfData);
181 E : if (!ReadImage(import_name, &hint, sizeof(hint)) ||
182 : !ReadImageString(import_name + sizeof(hint), &function_name)) {
183 i : LOG(ERROR) << "Unable to read import function hint or name.";
184 i : return false;
185 : }
186 : }
187 :
188 E : dll.functions.push_back(ImportInfo());
189 E : ImportInfo& info = dll.functions.back();
190 E : info.function = function_name;
191 E : info.ordinal = ordinal;
192 E : info.hint = hint;
193 :
194 E : int_offs += sizeof(int_thunk);
195 E : iat_offs += sizeof(iat_thunk);
196 E : }
197 E : }
198 :
199 E : return true;
200 E : }
201 :
202 : template <class ImageNtHeaders, DWORD MagicValidation>
203 : bool PEFileBase<ImageNtHeaders, MagicValidation>::DecodeExports(
204 E : ExportInfoVector* exports) const {
205 E : DCHECK_NE(static_cast<ExportInfoVector*>(NULL), exports);
206 :
207 : IMAGE_DATA_DIRECTORY dir = nt_headers_->OptionalHeader.
208 E : DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
209 E : RelativeAddress addr(dir.VirtualAddress);
210 E : RelativeAddress end(addr + dir.Size);
211 :
212 E : if (addr.value() == 0)
213 i : return true;
214 :
215 E : const IMAGE_EXPORT_DIRECTORY* export_dir = NULL;
216 E : if (!GetImageData(addr, sizeof(export_dir), &export_dir)) {
217 i : LOG(ERROR) << "Unable to read export directory.";
218 i : return false;
219 : }
220 :
221 E : const RelativeAddress* functions = NULL;
222 E : if (!GetImageData(RelativeAddress(export_dir->AddressOfFunctions),
223 : sizeof(*functions) * export_dir->NumberOfFunctions,
224 : &functions)) {
225 i : LOG(ERROR) << "Unable to read export functions.";
226 i : return false;
227 : }
228 :
229 E : const RelativeAddress* names = NULL;
230 E : if (!GetImageData(RelativeAddress(export_dir->AddressOfNames),
231 : sizeof(*functions) * export_dir->NumberOfNames,
232 : &names)) {
233 i : LOG(ERROR) << "Unable to read export names.";
234 i : return false;
235 : }
236 :
237 E : const WORD* name_ordinals = NULL;
238 E : if (!GetImageData(RelativeAddress(export_dir->AddressOfNameOrdinals),
239 : sizeof(*functions) * export_dir->NumberOfNames,
240 : &name_ordinals)) {
241 i : LOG(ERROR) << "Unable to read name ordinals.";
242 i : return false;
243 : }
244 :
245 E : for (uint16_t index = 0; index < export_dir->NumberOfFunctions; ++index) {
246 : // Is it a blank entry?
247 E : if (functions[index] != RelativeAddress(0)) {
248 E : ExportInfo info;
249 E : info.ordinal = index + 1;
250 :
251 E : RelativeAddress function = functions[index];
252 : // Is it a forward?
253 E : if (function >= addr && function < end) {
254 E : if (!ReadImageString(function, &info.forward)) {
255 i : LOG(ERROR) << "Unable to read export forward string.";
256 i : return false;
257 : }
258 E : } else {
259 E : info.function = function;
260 : }
261 :
262 : // Does it have a name?
263 E : for (uint16_t i = 0; i < export_dir->NumberOfNames; ++i) {
264 E : if (name_ordinals[i] == index) {
265 E : if (!ReadImageString(names[i], &info.name)) {
266 i : LOG(ERROR) << "Unable to read export name.";
267 i : return false;
268 : }
269 E : break;
270 : }
271 E : }
272 :
273 E : exports->push_back(info);
274 E : }
275 E : }
276 :
277 E : return true;
278 E : }
279 :
280 : template <class ImageNtHeaders, DWORD MagicValidation>
281 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Translate(
282 E : RelativeAddress rel, AbsoluteAddress* abs) const {
283 E : DCHECK_NE(static_cast<AbsoluteAddress*>(NULL), abs);
284 E : if (rel.value() >= nt_headers_->OptionalHeader.SizeOfImage)
285 E : return false;
286 E : abs->set_value(rel.value() + nt_headers_->OptionalHeader.ImageBase);
287 E : return true;
288 E : }
289 :
290 : template <class ImageNtHeaders, DWORD MagicValidation>
291 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Translate(
292 E : AbsoluteAddress abs, RelativeAddress* rel) const {
293 E : DCHECK_NE(static_cast<RelativeAddress*>(NULL), rel);
294 E : uintptr_t rel_addr = AbsToRelDisplacement(abs.value());
295 E : if (rel_addr >= nt_headers_->OptionalHeader.SizeOfImage)
296 E : return false;
297 E : rel->set_value(rel_addr);
298 E : return true;
299 E : }
300 :
301 : template <class ImageNtHeaders, DWORD MagicValidation>
302 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Translate(
303 E : FileOffsetAddress offs, RelativeAddress* rel) const {
304 E : DCHECK_NE(static_cast<RelativeAddress*>(NULL), rel);
305 :
306 : // The first "previous section" is the headers.
307 E : RelativeAddress previous_section_start(0);
308 E : FileOffsetAddress previous_section_file_start(0);
309 E : const IMAGE_SECTION_HEADER* previous_section = NULL;
310 E : for (size_t i = 0; i < nt_headers_->FileHeader.NumberOfSections; ++i) {
311 E : if (offs.value() < section_headers_[i].PointerToRawData)
312 E : break;
313 :
314 E : previous_section_start.set_value(section_headers_[i].VirtualAddress);
315 E : previous_section_file_start.set_value(
316 : section_headers_[i].PointerToRawData);
317 E : previous_section = section_headers_ + i;
318 E : }
319 :
320 E : size_t section_offset = offs - previous_section_file_start;
321 E : if (previous_section != NULL &&
322 : section_offset >= previous_section->SizeOfRawData) {
323 E : return false;
324 : }
325 :
326 E : *rel = previous_section_start + section_offset;
327 :
328 E : return true;
329 E : }
330 :
331 : template <class ImageNtHeaders, DWORD MagicValidation>
332 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Translate(
333 E : RelativeAddress rel, FileOffsetAddress* offs) const {
334 E : DCHECK_NE(static_cast<FileOffsetAddress*>(NULL), offs);
335 :
336 : // In the headers?
337 E : if (rel.value() < section_header(0)->VirtualAddress) {
338 E : offs->set_value(rel.value());
339 E : return true;
340 : }
341 :
342 : // Find the section in which this address lies.
343 E : const IMAGE_SECTION_HEADER* section = GetSectionHeader(rel, 1);
344 E : if (section == NULL)
345 E : return false;
346 :
347 : // Calculate the offset of this address and ensure it can be expressed as
348 : // a file offset (lies in the explicit data part of the section, not the
349 : // implicit virtual data at the end).
350 E : size_t section_offset = rel.value() - section->VirtualAddress;
351 E : if (section_offset >= section->SizeOfRawData)
352 E : return false;
353 :
354 E : offs->set_value(section->PointerToRawData + section_offset);
355 :
356 E : return true;
357 E : }
358 :
359 : template <class ImageNtHeaders, DWORD MagicValidation>
360 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Translate(
361 : RelativeAddress relative_address,
362 E : SectionOffsetAddress* section_offset_address) const {
363 E : DCHECK_NE(static_cast<SectionOffsetAddress*>(NULL), section_offset_address);
364 :
365 : // In the headers?
366 E : if (relative_address.value() < section_header(0)->VirtualAddress) {
367 E : section_offset_address->set_section_id(0);
368 E : section_offset_address->set_offset(relative_address.value());
369 E : return true;
370 : }
371 :
372 : // Find the section in which this address lies.
373 E : size_t section_id = GetSectionIndex(relative_address, 1);
374 E : if (section_id == kInvalidSection)
375 i : return false;
376 E : const IMAGE_SECTION_HEADER* section = section_header(section_id);
377 :
378 : // Calculate the offset of this address and ensure it can be expressed as
379 : // a section offset (lies in the explicit data part of the section, not the
380 : // implicit virtual data at the end).
381 E : uint32_t section_offset = relative_address.value() - section->VirtualAddress;
382 E : if (section_offset >= section->SizeOfRawData)
383 i : return false;
384 :
385 : // The offset of the first explicitly defined section is 1. The index 0
386 : // is used for addresses before the first section.
387 E : section_offset_address->set_section_id(section_id + 1);
388 E : section_offset_address->set_offset(section_offset);
389 :
390 E : return true;
391 E : }
392 :
393 : template <class ImageNtHeaders, DWORD MagicValidation>
394 : bool PEFileBase<ImageNtHeaders, MagicValidation>::ReadImage(
395 E : AbsoluteAddress addr, void* data, size_t len) const {
396 E : RelativeAddress rel;
397 E : if (!Translate(addr, &rel))
398 i : return false;
399 :
400 E : return ReadImage(rel, data, len);
401 E : }
402 :
403 : template <class ImageNtHeaders, DWORD MagicValidation>
404 : bool PEFileBase<ImageNtHeaders, MagicValidation>::ReadImageString(
405 E : AbsoluteAddress addr, std::string* str) const {
406 E : RelativeAddress rel;
407 E : if (!Translate(addr, &rel))
408 i : return false;
409 :
410 E : return ReadImageString(rel, str);
411 E : }
412 :
413 : template <class ImageNtHeaders, DWORD MagicValidation>
414 : const uint8_t* PEFileBase<ImageNtHeaders, MagicValidation>::GetImageData(
415 : AbsoluteAddress addr,
416 E : size_t len) const {
417 E : RelativeAddress rel;
418 E : if (Translate(addr, &rel))
419 E : return GetImageData(rel, len);
420 :
421 i : return NULL;
422 E : }
423 :
424 : template <class ImageNtHeaders, DWORD MagicValidation>
425 : uint8_t* PEFileBase<ImageNtHeaders, MagicValidation>::GetImageData(
426 : AbsoluteAddress addr,
427 E : size_t len) {
428 E : return const_cast<uint8_t*>(
429 : static_cast<const PEFileBase<ImageNtHeaders, MagicValidation>*>(this)
430 : ->GetImageData(addr, len));
431 E : }
432 :
433 : template <class ImageNtHeaders, DWORD MagicValidation>
434 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Contains(AbsoluteAddress addr,
435 E : size_t len) const {
436 E : RelativeAddress rel;
437 E : return Translate(addr, &rel) && Contains(rel, len);
438 E : }
439 :
440 : template <class ImageNtHeaders, DWORD MagicValidation>
441 : size_t PEFileBase<ImageNtHeaders, MagicValidation>::GetSectionIndex(
442 E : AbsoluteAddress addr, size_t len) const {
443 E : RelativeAddress rel;
444 E : if (!Translate(addr, &rel))
445 E : return kInvalidSection;
446 E : return GetSectionIndex(rel, len);
447 E : }
448 :
449 : template <class ImageNtHeaders, DWORD MagicValidation>
450 : const IMAGE_SECTION_HEADER* PEFileBase<ImageNtHeaders,
451 : MagicValidation>::GetSectionHeader(
452 E : AbsoluteAddress addr, size_t len) const {
453 E : RelativeAddress rel;
454 E : if (!Translate(addr, &rel))
455 i : return NULL;
456 E : return GetSectionHeader(rel, len);
457 E : }
458 :
459 : template <class ImageNtHeaders, DWORD MagicValidation>
460 : size_t PEFileBase<ImageNtHeaders, MagicValidation>::GetSectionIndex(
461 E : const char* name) const {
462 E : size_t section_count = file_header_->NumberOfSections;
463 E : for (size_t i = 0; i < section_count; ++i) {
464 E : const IMAGE_SECTION_HEADER* header = section_headers_ + i;
465 : if (strncmp(reinterpret_cast<const char*>(header->Name), name,
466 E : IMAGE_SIZEOF_SHORT_NAME) == 0)
467 E : return i;
468 E : }
469 E : return kInvalidSection;
470 E : }
471 :
472 : template <class ImageNtHeaders, DWORD MagicValidation>
473 : const IMAGE_SECTION_HEADER* PEFileBase<ImageNtHeaders,
474 : MagicValidation>::GetSectionHeader(
475 E : const char* name) const {
476 E : size_t id = GetSectionIndex(name);
477 E : if (id == kInvalidSection)
478 E : return NULL;
479 E : return section_headers_ + id;
480 E : }
481 :
482 : template <class ImageNtHeaders, DWORD MagicValidation>
483 : const IMAGE_DOS_HEADER* PEFileBase<ImageNtHeaders,
484 E : MagicValidation>::dos_header() const {
485 E : return dos_header_;
486 E : }
487 :
488 : template <class ImageNtHeaders, DWORD MagicValidation>
489 : const ImageNtHeaders* PEFileBase<ImageNtHeaders,
490 E : MagicValidation>::nt_headers() const {
491 E : return nt_headers_;
492 E : }
493 :
494 : template <class ImageNtHeaders, DWORD MagicValidation>
495 : size_t PEFileBase<ImageNtHeaders, MagicValidation>::AbsToRelDisplacement(
496 E : size_t abs_disp) const {
497 E : return abs_disp - nt_headers_->OptionalHeader.ImageBase;
498 E : }
499 :
500 : template <class ImageNtHeaders, DWORD MagicValidation>
501 E : bool PEFileBase<ImageNtHeaders, MagicValidation>::ReadHeaders() {
502 : // Read the DOS header.
503 E : if (!parser_.GetAt(0, &dos_header_))
504 i : return false;
505 :
506 : // And the NT headers, checking alignment.
507 E : FileOffsetAddress nt_off(dos_header_->e_lfanew);
508 E : if (!parser_.GetAt(nt_off.value(), &nt_headers_))
509 i : return false;
510 :
511 E : FileOffsetAddress file_header_start(dos_header_->e_lfanew +
512 : offsetof(ImageNtHeaders, FileHeader));
513 E : if (!ReadCommonHeaders(file_header_start))
514 i : return false;
515 :
516 E : return nt_headers_->OptionalHeader.Magic == MagicValidation;
517 E : }
518 :
519 : template<class ImageNtHeaders, DWORD MagicValidation>
520 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Signature::IsConsistent(
521 E : const Signature& signature) const {
522 E : return IsConsistentExceptForChecksum(signature) &&
523 : module_checksum == signature.module_checksum;
524 E : }
525 :
526 : template<class ImageNtHeaders, DWORD MagicValidation>
527 : bool PEFileBase<ImageNtHeaders,
528 : MagicValidation>::Signature::IsConsistentExceptForChecksum(
529 E : const Signature& signature) const {
530 E : return base_address == signature.base_address &&
531 : module_size == signature.module_size &&
532 : module_time_date_stamp == signature.module_time_date_stamp;
533 E : }
534 :
535 : template<class ImageNtHeaders, DWORD MagicValidation>
536 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Signature::operator==(
537 E : const Signature& signature) const {
538 E : return path == signature.path && IsConsistent(signature);
539 E : }
540 :
541 : template<class ImageNtHeaders, DWORD MagicValidation>
542 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Signature::Save(
543 E : core::OutArchive* out_archive) const {
544 E : return out_archive->Save(path) &&
545 : out_archive->Save(base_address) &&
546 : out_archive->Save(module_size) &&
547 : out_archive->Save(module_time_date_stamp) &&
548 : out_archive->Save(module_checksum);
549 E : }
550 :
551 : template<class ImageNtHeaders, DWORD MagicValidation>
552 : bool PEFileBase<ImageNtHeaders, MagicValidation>::Signature::Load(
553 E : core::InArchive* in_archive) {
554 E : return in_archive->Load(&path) &&
555 : in_archive->Load(&base_address) &&
556 : in_archive->Load(&module_size) &&
557 : in_archive->Load(&module_time_date_stamp) &&
558 : in_archive->Load(&module_checksum);
559 E : }
560 :
561 : } // namespace pe
562 :
563 : #endif // SYZYGY_PE_PE_FILE_IMPL_H_
|