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