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/pe/pe_file_parser.h"
16 :
17 : #include <delayimp.h>
18 :
19 : #include "base/bind.h"
20 : #include "base/file_path.h"
21 : #include "base/native_library.h"
22 : #include "base/path_service.h"
23 : #include "base/string_util.h"
24 : #include "base/memory/scoped_ptr.h"
25 : #include "base/win/pe_image.h"
26 : #include "gmock/gmock.h"
27 : #include "gtest/gtest.h"
28 : #include "syzygy/core/unittest_util.h"
29 : #include "syzygy/pe/unittest_util.h"
30 :
31 : namespace pe {
32 :
33 : using block_graph::BlockGraph;
34 : using core::RelativeAddress;
35 : using testing::ContainerEq;
36 : using testing::Contains;
37 :
38 : namespace {
39 :
40 : // Exposes the protected methods for testing.
41 : class TestPEFileParser: public PEFileParser {
42 : public:
43 E : TestPEFileParser(const PEFile& image_file,
44 : BlockGraph::AddressSpace* address_space,
45 : AddReferenceCallback add_reference)
46 : : PEFileParser(image_file, address_space, add_reference) {
47 E : }
48 :
49 : // Expose as public for testing.
50 : using PEFileParser::ParseArchitectureDir;
51 : using PEFileParser::ParseBoundImportDir;
52 : using PEFileParser::ParseComDescriptorDir;
53 : using PEFileParser::ParseDebugDir;
54 : using PEFileParser::ParseDelayImportDir;
55 : using PEFileParser::ParseExceptionDir;
56 : using PEFileParser::ParseExportDir;
57 : using PEFileParser::ParseGlobalDir;
58 : using PEFileParser::ParseIatDir;
59 : using PEFileParser::ParseImageHeader;
60 : using PEFileParser::ParseImportDir;
61 : using PEFileParser::ParseLoadConfigDir;
62 : using PEFileParser::ParseRelocDir;
63 : using PEFileParser::ParseResourceDir;
64 : using PEFileParser::ParseSecurityDir;
65 : using PEFileParser::ParseTlsDir;
66 : };
67 :
68 : class PEFileParserTest: public testing::PELibUnitTest {
69 : typedef testing::PELibUnitTest Super;
70 : public:
71 E : PEFileParserTest() : address_space_(&image_), loaded_image_(NULL) {
72 E : }
73 :
74 E : virtual void SetUp() {
75 E : Super::SetUp();
76 :
77 : add_reference_ = base::Bind(&PEFileParserTest::AddReference,
78 E : base::Unretained(this));
79 : on_import_thunk_ = base::Bind(&PEFileParserTest::OnImportThunk,
80 E : base::Unretained(this));
81 :
82 E : ASSERT_TRUE(image_file_.Init(testing::GetExeRelativePath(kDllName)));
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(kDllName), &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(testing::GetExeRelativePath(kDllName),
243 E : &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 : 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 _MSC_VER == 1500 && defined(NDEBUG)
328 : // VC++ 2008 Release Build.
329 : static size_t kNumKernel32Symbols = 73;
330 : #elif _MSC_VER == 1500 && !defined(NDEBUG)
331 : // VC++ 2008 Debug/Coverage Build.
332 : static size_t kNumKernel32Symbols = 80;
333 : #elif _MSC_VER == 1600 && defined(NDEBUG)
334 : // VC++ 2010 Release Build.
335 : static size_t kNumKernel32Symbols = 68;
336 : #elif _MSC_VER == 1600 && !defined(NDEBUG)
337 : // VC++ 2010 Debug/Coverage build.
338 : static size_t kNumKernel32Symbols = 72;
339 : #else
340 : #error Unrecognized compiler version or build configuration.
341 : #endif
342 :
343 : // The number of expected symbols imported from export_dll.dll.
344 : static const size_t kNumExportDllSymbols = 2;
345 :
346 E : ImportMap expected_import_map;
347 E : expected_import_map["KERNEL32.dll"] = kNumKernel32Symbols;
348 E : expected_import_map["export_dll.dll"] = kNumExportDllSymbols;
349 E : EXPECT_THAT(import_map_, ContainerEq(expected_import_map));
350 :
351 E : EXPECT_EQ(kNumKernel32Symbols + kNumExportDllSymbols, import_set_.size());
352 : EXPECT_THAT(import_set_, Contains(std::make_pair(
353 E : std::string("KERNEL32.dll"), std::string("ExitProcess"))));
354 : EXPECT_THAT(import_set_, Contains(std::make_pair(
355 E : std::string("export_dll.dll"), std::string("function1"))));
356 E : }
357 :
358 E : TEST_F(PEFileParserTest, ParseDelayImportDir) {
359 E : TestPEFileParser parser(image_file_, &address_space_, add_reference_);
360 :
361 E : PEFileParser::PEHeader header;
362 E : EXPECT_TRUE(parser.ParseImageHeader(&header));
363 :
364 : const IMAGE_NT_HEADERS* nt_headers =
365 E : reinterpret_cast<const IMAGE_NT_HEADERS*>(header.nt_headers->data());
366 :
367 : const IMAGE_DATA_DIRECTORY& dir =
368 : nt_headers->OptionalHeader.DataDirectory[
369 E : IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT];
370 E : BlockGraph::Block* block = parser.ParseDelayImportDir(dir);
371 E : ASSERT_TRUE(block != NULL);
372 :
373 : // Test that we have the import descriptors we expect - we expect
374 : // the one delay import, plus the sentinel import descriptor to be
375 : // chunked out.
376 E : size_t num_descriptors = block->size() / sizeof(ImgDelayDescr);
377 E : ASSERT_EQ(2, num_descriptors);
378 E : ASSERT_TRUE(block->data() != NULL);
379 E : ASSERT_EQ(block->size(), block->data_size());
380 :
381 E : std::set<std::string> import_names;
382 E : for (size_t i = 0; i < num_descriptors - 1; ++i) {
383 E : size_t element_offset = sizeof(ImgDelayDescr) * i;
384 : BlockGraph::Block* name_block =
385 : FindReferencedBlock(block, element_offset +
386 E : offsetof(ImgDelayDescr, rvaDLLName));
387 E : ASSERT_TRUE(name_block != NULL);
388 :
389 : const char* name =
390 E : reinterpret_cast<const char*>(name_block->data());
391 E : EXPECT_TRUE(import_names.insert(name).second);
392 :
393 : // Now retrieve the IAT, INT and BoundIAT blocks.
394 : BlockGraph::Block* iat_block =
395 : FindReferencedBlock(block, element_offset +
396 E : offsetof(ImgDelayDescr, rvaIAT));
397 : BlockGraph::Block* int_block =
398 : FindReferencedBlock(block, element_offset +
399 E : offsetof(ImgDelayDescr, rvaINT));
400 : BlockGraph::Block* bound_iat_block =
401 : FindReferencedBlock(block, element_offset +
402 E : offsetof(ImgDelayDescr, rvaBoundIAT));
403 :
404 E : ASSERT_TRUE(iat_block != NULL);
405 E : ASSERT_TRUE(int_block != NULL);
406 E : ASSERT_TRUE(bound_iat_block != NULL);
407 :
408 E : ASSERT_EQ(iat_block->size(), int_block->size());
409 E : ASSERT_EQ(iat_block->size(), bound_iat_block->size());
410 E : ASSERT_EQ(iat_block->data_size(), int_block->data_size());
411 E : ASSERT_EQ(iat_block->data_size(), bound_iat_block->data_size());
412 :
413 : // Now check that each slot, save for the last one, in the INT
414 : // points to a name block or else is an ordinal.
415 E : size_t num_thunks = iat_block->data_size() / sizeof(IMAGE_THUNK_DATA) - 1;
416 : const IMAGE_THUNK_DATA* iat =
417 E : reinterpret_cast<const IMAGE_THUNK_DATA*>(int_block->data());
418 E : for (size_t i = 0; i < num_thunks; ++i) {
419 E : if (!IMAGE_ORDINAL(iat[i].u1.Ordinal)) {
420 i : size_t thunk_offset = sizeof(IMAGE_THUNK_DATA) * i;
421 i : ASSERT_TRUE(FindReferencedBlock(int_block, thunk_offset) != NULL);
422 : }
423 E : }
424 E : }
425 :
426 : // Check that the sentinel is all zero.
427 E : ImgDelayDescr zero = {};
428 : const ImgDelayDescr* sentinel =
429 : reinterpret_cast<const ImgDelayDescr*>(block->data()) +
430 E : num_descriptors - 1;
431 E : memcmp(sentinel, &zero, sizeof(zero));
432 :
433 E : std::set<std::string> expected;
434 E : expected.insert("ole32.dll");
435 E : EXPECT_THAT(import_names, ContainerEq(expected));
436 E : }
437 :
438 E : TEST_F(PEFileParserTest, ParseImage) {
439 E : TestPEFileParser parser(image_file_, &address_space_, add_reference_);
440 :
441 E : PEFileParser::PEHeader header;
442 E : EXPECT_TRUE(parser.ParseImage(&header));
443 :
444 : // Check that the DOS header was read successfully.
445 E : ASSERT_TRUE(header.dos_header != NULL);
446 E : ASSERT_GE(header.dos_header->size(), sizeof(IMAGE_DOS_HEADER));
447 E : ASSERT_EQ(BlockGraph::DATA_BLOCK, header.dos_header->type());
448 : // Check the underlying data.
449 E : ASSERT_GE(header.dos_header->data_size(), sizeof(IMAGE_DOS_HEADER));
450 : const IMAGE_DOS_HEADER* dos_header =
451 E : reinterpret_cast<const IMAGE_DOS_HEADER*>(header.dos_header->data());
452 E : ASSERT_TRUE(dos_header != NULL);
453 E : ASSERT_EQ(IMAGE_DOS_SIGNATURE, dos_header->e_magic);
454 :
455 : // Check the NT headers.
456 E : ASSERT_TRUE(header.nt_headers != NULL);
457 E : ASSERT_GT(header.nt_headers->size(), sizeof(IMAGE_NT_HEADERS));
458 E : ASSERT_EQ(header.nt_headers->data_size(), header.nt_headers->size());
459 E : ASSERT_EQ(BlockGraph::DATA_BLOCK, header.nt_headers->type());
460 : const IMAGE_NT_HEADERS* nt_headers =
461 E : reinterpret_cast<const IMAGE_NT_HEADERS*>(header.nt_headers->data());
462 E : ASSERT_TRUE(nt_headers != NULL);
463 E : ASSERT_EQ(IMAGE_NT_OPTIONAL_HDR32_MAGIC, nt_headers->OptionalHeader.Magic);
464 :
465 E : const IMAGE_SECTION_HEADER* section_headers = NULL;
466 : // Check that the data accounts for the image section headers.
467 : ASSERT_EQ(nt_headers->FileHeader.NumberOfSections * sizeof(*section_headers) +
468 E : sizeof(*nt_headers), header.nt_headers->data_size());
469 :
470 : section_headers =
471 E : reinterpret_cast<const IMAGE_SECTION_HEADER*>(nt_headers + 1);
472 :
473 : // Now check the various data directory sections we expect to be non NULL.
474 : // We know the test dll has exports.
475 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
476 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_EXPORT]));
477 : // And imports.
478 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
479 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT]));
480 : // And resources.
481 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
482 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_RESOURCE]));
483 : // And relocs.
484 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
485 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC]));
486 : // And a debug directory.
487 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
488 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_DEBUG]));
489 : // And a tls directory?
490 : // TODO(siggi): add some TLS data to the test DLL.
491 : // EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
492 : // header.data_directory[IMAGE_DIRECTORY_ENTRY_TLS]));
493 : // And a load configuration directory.
494 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
495 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG]));
496 : // And a delay import directory.
497 : EXPECT_NO_FATAL_FAILURE(AssertDataDirectoryEntryValid(
498 E : header.data_directory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT]));
499 E : }
500 :
501 : } // namespace pe
|