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/pe/pe_file_parser.h"
16 :
17 : #include "base/bind.h"
18 : #include "base/native_library.h"
19 : #include "base/path_service.h"
20 : #include "base/string_util.h"
21 : #include "base/files/file_path.h"
22 : #include "base/memory/scoped_ptr.h"
23 : #include "base/win/pe_image.h"
24 : #include "gmock/gmock.h"
25 : #include "gtest/gtest.h"
26 : #include "syzygy/core/unittest_util.h"
27 : #include "syzygy/pe/unittest_util.h"
28 :
29 : namespace pe {
30 :
31 : using block_graph::BlockGraph;
32 : using core::RelativeAddress;
33 : using testing::ContainerEq;
34 : using testing::Contains;
35 :
36 : namespace {
37 :
38 : // Exposes the protected methods for testing.
39 : class TestPEFileParser: public PEFileParser {
40 : public:
41 E : TestPEFileParser(const PEFile& image_file,
42 : BlockGraph::AddressSpace* address_space,
43 : AddReferenceCallback add_reference)
44 : : PEFileParser(image_file, address_space, add_reference) {
45 E : }
46 :
47 : // Expose as public for testing.
48 : using PEFileParser::ParseArchitectureDir;
49 : using PEFileParser::ParseBoundImportDir;
50 : using PEFileParser::ParseComDescriptorDir;
51 : using PEFileParser::ParseDebugDir;
52 : using PEFileParser::ParseDelayImportDir;
53 : using PEFileParser::ParseExceptionDir;
54 : using PEFileParser::ParseExportDir;
55 : using PEFileParser::ParseGlobalDir;
56 : using PEFileParser::ParseIatDir;
57 : using PEFileParser::ParseImageHeader;
58 : using PEFileParser::ParseImportDir;
59 : using PEFileParser::ParseLoadConfigDir;
60 : using PEFileParser::ParseRelocDir;
61 : using PEFileParser::ParseResourceDir;
62 : using PEFileParser::ParseSecurityDir;
63 : using PEFileParser::ParseTlsDir;
64 : };
65 :
66 : class PEFileParserTest: public testing::PELibUnitTest {
67 : typedef testing::PELibUnitTest Super;
68 :
69 : public:
70 E : PEFileParserTest() : address_space_(&image_), loaded_image_(NULL) {
71 E : }
72 :
73 E : virtual void SetUp() {
74 E : Super::SetUp();
75 :
76 : add_reference_ = base::Bind(&PEFileParserTest::AddReference,
77 E : base::Unretained(this));
78 : on_import_thunk_ = base::Bind(&PEFileParserTest::OnImportThunk,
79 E : base::Unretained(this));
80 :
81 E : ASSERT_TRUE(image_file_.Init(testing::GetExeRelativePath(
82 : testing::kTestDllName)));
83 E : }
84 :
85 E : virtual void TearDown() {
86 E : if (loaded_image_ != NULL)
87 E : base::UnloadNativeLibrary(loaded_image_);
88 E : loaded_image_ = NULL;
89 :
90 E : Super::TearDown();
91 E : }
92 :
93 : bool AddReference(RelativeAddress src,
94 : BlockGraph::ReferenceType type,
95 : BlockGraph::Size size,
96 E : RelativeAddress dst) {
97 E : Reference ref = { type, size, dst };
98 E : bool inserted = references_.insert(std::make_pair(src, ref)).second;
99 E : EXPECT_TRUE(inserted);
100 E : return inserted;
101 E : }
102 :
103 : bool OnImportThunk(const char* module_name,
104 : const char* symbol_name,
105 E : BlockGraph::Block* thunk) {
106 E : EXPECT_TRUE(module_name != NULL);
107 E : EXPECT_TRUE(symbol_name != NULL);
108 E : EXPECT_TRUE(thunk != NULL);
109 E : import_map_[module_name]++;
110 E : EXPECT_TRUE(import_set_.insert(
111 : std::make_pair(std::string(module_name),
112 : std::string(symbol_name))).second);
113 E : return true;
114 E : }
115 :
116 : // Assert that an exported function in the test_dll is referenced
117 : // in the image.
118 E : bool ExportIsReferenced(const char* function_name_or_ordinal) {
119 E : if (loaded_image_ == NULL) {
120 i : std::string error;
121 : loaded_image_ = base::LoadNativeLibrary(
122 i : testing::GetExeRelativePath(testing::kTestDllName), &error);
123 i : }
124 :
125 E : EXPECT_TRUE(loaded_image_ != NULL);
126 E : if (loaded_image_ == NULL)
127 i : return false;
128 :
129 : void* function = base::GetFunctionPointerFromNativeLibrary(
130 E : loaded_image_, function_name_or_ordinal);
131 :
132 : RelativeAddress addr(reinterpret_cast<const char*>(function) -
133 E : reinterpret_cast<const char*>(loaded_image_));
134 :
135 E : ReferenceMap::const_iterator it(references_.begin());
136 E : for (; it != references_.end(); ++it) {
137 E : if (it->second.dst == addr)
138 E : return true;
139 E : }
140 :
141 i : return false;
142 E : }
143 :
144 E : void AssertDataDirectoryEntryValid(BlockGraph::Block* block) {
145 E : ASSERT_TRUE(block != NULL);
146 E : ASSERT_NE(0u, block->size());
147 E : ASSERT_EQ(block->size(), block->data_size());
148 E : ASSERT_TRUE(block->data() != NULL);
149 E : }
150 :
151 : // Locate block pointed to by the reference at @p offset into @p block.
152 : // @returns the block in question, or NULL if no such block.
153 : BlockGraph::Block* FindReferencedBlock(BlockGraph::Block* block,
154 E : BlockGraph::Offset offset) {
155 E : ReferenceMap::const_iterator it(references_.find(block->addr() + offset));
156 E : if (it == references_.end())
157 i : return NULL;
158 :
159 E : return address_space_.GetBlockByAddress(it->second.dst);
160 E : }
161 :
162 : protected:
163 : struct Reference {
164 : BlockGraph::ReferenceType type;
165 : BlockGraph::Size size;
166 : RelativeAddress dst;
167 : };
168 :
169 : typedef std::map<RelativeAddress, Reference> ReferenceMap;
170 : ReferenceMap references_;
171 :
172 : // This is used to count the number of imported symbols per imported module,
173 : // and is populated by the OnImportThunk callback.
174 : typedef std::map<std::string, size_t> ImportMap;
175 : typedef std::set<std::pair<std::string, std::string>> ImportSet;
176 : ImportMap import_map_;
177 : ImportSet import_set_;
178 :
179 : PEFileParser::AddReferenceCallback add_reference_;
180 : PEFileParser::OnImportThunkCallback on_import_thunk_;
181 : PEFile image_file_;
182 : BlockGraph image_;
183 : BlockGraph::AddressSpace address_space_;
184 :
185 : base::NativeLibrary loaded_image_;
186 : };
187 :
188 : } // namespace
189 :
190 E : TEST_F(PEFileParserTest, ParseImageHeader) {
191 E : TestPEFileParser parser(image_file_, &address_space_, add_reference_);
192 :
193 E : PEFileParser::PEHeader header;
194 E : EXPECT_TRUE(parser.ParseImageHeader(&header));
195 :
196 : // Check that the DOS header was read successfully.
197 E : ASSERT_TRUE(header.dos_header != NULL);
198 E : ASSERT_GE(header.dos_header->size(), sizeof(IMAGE_DOS_HEADER));
199 E : ASSERT_EQ(BlockGraph::DATA_BLOCK, header.dos_header->type());
200 : // Check the underlying data.
201 E : ASSERT_GE(header.dos_header->data_size(), sizeof(IMAGE_DOS_HEADER));
202 : const IMAGE_DOS_HEADER* dos_header =
203 E : reinterpret_cast<const IMAGE_DOS_HEADER*>(header.dos_header->data());
204 E : ASSERT_TRUE(dos_header != NULL);
205 E : ASSERT_EQ(IMAGE_DOS_SIGNATURE, dos_header->e_magic);
206 :
207 : // Check that the DOS header references the NT headers.
208 : ASSERT_EQ(header.nt_headers,
209 : FindReferencedBlock(header.dos_header,
210 E : offsetof(IMAGE_DOS_HEADER, e_lfanew)));
211 :
212 : // Check the NT headers.
213 E : ASSERT_TRUE(header.nt_headers != NULL);
214 E : ASSERT_GT(header.nt_headers->size(), sizeof(IMAGE_NT_HEADERS));
215 E : ASSERT_EQ(header.nt_headers->data_size(), header.nt_headers->size());
216 E : ASSERT_EQ(BlockGraph::DATA_BLOCK, header.nt_headers->type());
217 : const IMAGE_NT_HEADERS* nt_headers =
218 E : reinterpret_cast<const IMAGE_NT_HEADERS*>(header.nt_headers->data());
219 E : ASSERT_TRUE(nt_headers != NULL);
220 E : ASSERT_EQ(IMAGE_NT_OPTIONAL_HDR32_MAGIC, nt_headers->OptionalHeader.Magic);
221 :
222 E : const IMAGE_SECTION_HEADER* section_headers = NULL;
223 : // Check that the data accounts for the image section headers.
224 : ASSERT_EQ(nt_headers->FileHeader.NumberOfSections * sizeof(*section_headers) +
225 E : sizeof(*nt_headers), header.nt_headers->data_size());
226 E : }
227 :
228 E : TEST_F(PEFileParserTest, ParseExportDir) {
229 E : TestPEFileParser parser(image_file_, &address_space_, add_reference_);
230 :
231 E : PEFileParser::PEHeader header;
232 E : EXPECT_TRUE(parser.ParseImageHeader(&header));
233 :
234 : const IMAGE_NT_HEADERS* nt_headers =
235 E : reinterpret_cast<const IMAGE_NT_HEADERS*>(header.nt_headers->data());
236 :
237 : const IMAGE_DATA_DIRECTORY& dir =
238 E : nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
239 E : EXPECT_TRUE(parser.ParseExportDir(dir) != NULL);
240 :
241 E : std::string error;
242 : loaded_image_ = base::LoadNativeLibrary(
243 E : testing::GetExeRelativePath(testing::kTestDllName), &error);
244 E : ASSERT_TRUE(loaded_image_ != NULL);
245 :
246 E : ASSERT_TRUE(ExportIsReferenced("function1"));
247 : // function2 is exported by ordinal only.
248 E : ASSERT_TRUE(ExportIsReferenced(reinterpret_cast<const char*>(7)));
249 E : ASSERT_TRUE(ExportIsReferenced("function3"));
250 E : }
251 :
252 E : TEST_F(PEFileParserTest, ParseImportDir) {
253 E : TestPEFileParser parser(image_file_, &address_space_, add_reference_);
254 E : parser.set_on_import_thunk(on_import_thunk_);
255 :
256 E : PEFileParser::PEHeader header;
257 E : EXPECT_TRUE(parser.ParseImageHeader(&header));
258 :
259 : const IMAGE_NT_HEADERS* nt_headers =
260 E : reinterpret_cast<const IMAGE_NT_HEADERS*>(header.nt_headers->data());
261 :
262 : const IMAGE_DATA_DIRECTORY& dir =
263 E : nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
264 E : BlockGraph::Block* block = parser.ParseImportDir(dir);
265 E : ASSERT_TRUE(block != NULL);
266 :
267 : // Test that we have the two import descriptors we expect, plus the sentinel.
268 E : size_t num_descriptors = block->size() / sizeof(IMAGE_IMPORT_DESCRIPTOR);
269 E : ASSERT_EQ(3, num_descriptors);
270 E : ASSERT_TRUE(block->data() != NULL);
271 E : ASSERT_EQ(block->size(), block->data_size());
272 :
273 E : std::set<std::string> import_names;
274 E : for (size_t i = 0; i < num_descriptors - 1; ++i) {
275 E : size_t element_offset = sizeof(IMAGE_IMPORT_DESCRIPTOR) * i;
276 : BlockGraph::Block* name_block =
277 : FindReferencedBlock(block, element_offset +
278 E : offsetof(IMAGE_IMPORT_DESCRIPTOR, Name));
279 E : ASSERT_TRUE(name_block != NULL);
280 :
281 : const char* name =
282 E : reinterpret_cast<const char*>(name_block->data());
283 E : EXPECT_TRUE(import_names.insert(name).second);
284 :
285 : // Now retrieve the IAT and INT blocks.
286 : BlockGraph::Block* iat_block =
287 : FindReferencedBlock(block, element_offset +
288 E : offsetof(IMAGE_IMPORT_DESCRIPTOR, FirstThunk));
289 : BlockGraph::Block* int_block =
290 : FindReferencedBlock(block, element_offset +
291 E : offsetof(IMAGE_IMPORT_DESCRIPTOR, OriginalFirstThunk));
292 :
293 E : ASSERT_TRUE(iat_block != NULL);
294 E : ASSERT_TRUE(int_block != NULL);
295 E : ASSERT_EQ(iat_block->size(), int_block->size());
296 E : ASSERT_EQ(iat_block->data_size(), int_block->data_size());
297 : ASSERT_EQ(0,
298 E : memcmp(iat_block->data(), int_block->data(), iat_block->data_size()));
299 :
300 : // Now check that each slot, save for the last one, in the IAT/INT
301 : // points to a name block or else is an ordinal.
302 E : size_t num_thunks = iat_block->data_size() / sizeof(IMAGE_THUNK_DATA) - 1;
303 : const IMAGE_THUNK_DATA* iat =
304 E : reinterpret_cast<const IMAGE_THUNK_DATA*>(iat_block->data());
305 E : for (size_t i = 0; i < num_thunks; ++i) {
306 E : if (!IMAGE_ORDINAL(iat[i].u1.Ordinal)) {
307 i : size_t thunk_offset = sizeof(IMAGE_THUNK_DATA) * i;
308 i : ASSERT_TRUE(FindReferencedBlock(iat_block, thunk_offset) != NULL);
309 i : ASSERT_TRUE(FindReferencedBlock(int_block, thunk_offset) != NULL);
310 : }
311 E : }
312 E : }
313 :
314 : // Check that the sentinel is all zero.
315 E : IMAGE_IMPORT_DESCRIPTOR zero = {};
316 : const IMAGE_IMPORT_DESCRIPTOR* sentinel =
317 : reinterpret_cast<const IMAGE_IMPORT_DESCRIPTOR*>(block->data()) +
318 E : num_descriptors - 1;
319 E : EXPECT_EQ(0, memcmp(sentinel, &zero, sizeof(zero)));
320 :
321 E : std::set<std::string> expected;
322 E : expected.insert("KERNEL32.dll");
323 E : expected.insert("export_dll.dll");
324 E : EXPECT_THAT(import_names, ContainerEq(expected));
325 :
326 : // The number of expected symbols imported from kernel32.dll.
327 : #if defined(NDEBUG)
328 : // VC++ 2010 Release Build.
329 : static size_t kNumKernel32Symbols = 68;
330 : #else
331 : // VC++ 2010 Debug/Coverage build.
332 : static size_t kNumKernel32Symbols = 70;
333 : #endif
334 :
335 : // The number of expected symbols imported from export_dll.dll.
336 : static const size_t kNumExportDllSymbols = 3;
337 :
338 E : ImportMap expected_import_map;
339 E : expected_import_map["KERNEL32.dll"] = kNumKernel32Symbols;
340 E : expected_import_map["export_dll.dll"] = kNumExportDllSymbols;
341 E : EXPECT_THAT(import_map_, ContainerEq(expected_import_map));
342 :
343 E : EXPECT_EQ(kNumKernel32Symbols + kNumExportDllSymbols, import_set_.size());
344 : EXPECT_THAT(import_set_, Contains(std::make_pair(
345 E : std::string("KERNEL32.dll"), std::string("ExitProcess"))));
346 : EXPECT_THAT(import_set_, Contains(std::make_pair(
347 E : std::string("export_dll.dll"), std::string("function1"))));
348 E : }
349 :
350 E : TEST_F(PEFileParserTest, ParseDelayImportDir) {
351 E : TestPEFileParser parser(image_file_, &address_space_, add_reference_);
352 :
353 E : PEFileParser::PEHeader header;
354 E : EXPECT_TRUE(parser.ParseImageHeader(&header));
355 :
356 : const IMAGE_NT_HEADERS* nt_headers =
357 E : reinterpret_cast<const IMAGE_NT_HEADERS*>(header.nt_headers->data());
358 :
359 : const IMAGE_DATA_DIRECTORY& dir =
360 : nt_headers->OptionalHeader.DataDirectory[
361 E : IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT];
362 E : BlockGraph::Block* block = parser.ParseDelayImportDir(dir);
363 E : ASSERT_TRUE(block != NULL);
364 :
365 : // Test that we have the import descriptors we expect - we expect
366 : // the one delay import, plus the sentinel import descriptor to be
367 : // chunked out.
368 E : size_t num_descriptors = block->size() / sizeof(ImgDelayDescr);
369 E : ASSERT_EQ(2, num_descriptors);
370 E : ASSERT_TRUE(block->data() != NULL);
371 E : ASSERT_EQ(block->size(), block->data_size());
372 :
373 E : std::set<std::string> import_names;
374 E : for (size_t i = 0; i < num_descriptors - 1; ++i) {
375 E : size_t element_offset = sizeof(ImgDelayDescr) * i;
376 : BlockGraph::Block* name_block =
377 : FindReferencedBlock(block, element_offset +
378 E : offsetof(ImgDelayDescr, rvaDLLName));
379 E : ASSERT_TRUE(name_block != NULL);
380 :
381 : const char* name =
382 E : reinterpret_cast<const char*>(name_block->data());
383 E : EXPECT_TRUE(import_names.insert(name).second);
384 :
385 : // Now retrieve the IAT, INT and BoundIAT blocks.
386 : BlockGraph::Block* iat_block =
387 : FindReferencedBlock(block, element_offset +
388 E : offsetof(ImgDelayDescr, rvaIAT));
389 : BlockGraph::Block* int_block =
390 : FindReferencedBlock(block, element_offset +
391 E : offsetof(ImgDelayDescr, rvaINT));
392 : BlockGraph::Block* bound_iat_block =
393 : FindReferencedBlock(block, element_offset +
394 E : offsetof(ImgDelayDescr, rvaBoundIAT));
395 :
396 E : ASSERT_TRUE(iat_block != NULL);
397 E : ASSERT_TRUE(int_block != NULL);
398 E : ASSERT_TRUE(bound_iat_block != NULL);
399 :
400 E : ASSERT_EQ(iat_block->size(), int_block->size());
401 E : ASSERT_EQ(iat_block->size(), bound_iat_block->size());
402 E : ASSERT_EQ(iat_block->data_size(), int_block->data_size());
403 E : ASSERT_EQ(iat_block->data_size(), bound_iat_block->data_size());
404 :
405 : // Now check that each slot, save for the last one, in the INT
406 : // points to a name block or else is an ordinal.
407 E : size_t num_thunks = iat_block->data_size() / sizeof(IMAGE_THUNK_DATA) - 1;
408 : const IMAGE_THUNK_DATA* iat =
409 E : reinterpret_cast<const IMAGE_THUNK_DATA*>(int_block->data());
410 E : for (size_t i = 0; i < num_thunks; ++i) {
411 E : if (!IMAGE_ORDINAL(iat[i].u1.Ordinal)) {
412 i : size_t thunk_offset = sizeof(IMAGE_THUNK_DATA) * i;
413 i : ASSERT_TRUE(FindReferencedBlock(int_block, thunk_offset) != NULL);
414 : }
415 E : }
416 E : }
417 :
418 : // Check that the sentinel is all zero.
419 E : ImgDelayDescr zero = {};
420 : const ImgDelayDescr* sentinel =
421 : reinterpret_cast<const ImgDelayDescr*>(block->data()) +
422 E : num_descriptors - 1;
423 E : EXPECT_EQ(0, memcmp(sentinel, &zero, sizeof(zero)));
424 :
425 E : std::set<std::string> expected;
426 E : expected.insert("ole32.dll");
427 E : EXPECT_THAT(import_names, ContainerEq(expected));
428 E : }
429 :
430 E : TEST_F(PEFileParserTest, ParseImage) {
431 E : TestPEFileParser parser(image_file_, &address_space_, add_reference_);
432 :
433 E : PEFileParser::PEHeader header;
434 E : EXPECT_TRUE(parser.ParseImage(&header));
435 :
436 : // Check that the DOS header was read successfully.
437 E : ASSERT_TRUE(header.dos_header != NULL);
438 E : ASSERT_GE(header.dos_header->size(), sizeof(IMAGE_DOS_HEADER));
439 E : ASSERT_EQ(BlockGraph::DATA_BLOCK, header.dos_header->type());
440 : // Check the underlying data.
441 E : ASSERT_GE(header.dos_header->data_size(), sizeof(IMAGE_DOS_HEADER));
442 : const IMAGE_DOS_HEADER* dos_header =
443 E : reinterpret_cast<const IMAGE_DOS_HEADER*>(header.dos_header->data());
444 E : ASSERT_TRUE(dos_header != NULL);
445 E : ASSERT_EQ(IMAGE_DOS_SIGNATURE, dos_header->e_magic);
446 :
447 : // Check the NT headers.
448 E : ASSERT_TRUE(header.nt_headers != NULL);
449 E : ASSERT_GT(header.nt_headers->size(), sizeof(IMAGE_NT_HEADERS));
450 E : ASSERT_EQ(header.nt_headers->data_size(), header.nt_headers->size());
451 E : ASSERT_EQ(BlockGraph::DATA_BLOCK, header.nt_headers->type());
452 : const IMAGE_NT_HEADERS* nt_headers =
453 E : reinterpret_cast<const IMAGE_NT_HEADERS*>(header.nt_headers->data());
454 E : ASSERT_TRUE(nt_headers != NULL);
455 E : ASSERT_EQ(IMAGE_NT_OPTIONAL_HDR32_MAGIC, nt_headers->OptionalHeader.Magic);
456 :
457 E : const IMAGE_SECTION_HEADER* section_headers = NULL;
458 : // Check that the data accounts for the image section headers.
459 : ASSERT_EQ(nt_headers->FileHeader.NumberOfSections * sizeof(*section_headers) +
460 E : sizeof(*nt_headers), header.nt_headers->data_size());
461 :
462 : section_headers =
463 E : reinterpret_cast<const IMAGE_SECTION_HEADER*>(nt_headers + 1);
464 :
465 : // Now check the various data directory sections we expect to be non NULL.
466 : // We know the test dll has exports.
467 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
468 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_EXPORT]));
469 : // And imports.
470 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
471 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT]));
472 : // And resources.
473 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
474 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_RESOURCE]));
475 : // And relocs.
476 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
477 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC]));
478 : // And a debug directory.
479 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
480 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_DEBUG]));
481 : // And a tls directory?
482 : // TODO(siggi): add some TLS data to the test DLL.
483 : // EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
484 : // header.data_directory[IMAGE_DIRECTORY_ENTRY_TLS]));
485 : // And a load configuration directory.
486 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
487 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG]));
488 : // And a delay import directory.
489 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
490 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT]));
491 E : }
492 :
493 : } // namespace pe
|