kernel32: Add a test to show that Windows changes the WRITECOPY to WRITE protection...
[wine] / dlls / kernel32 / tests / loader.c
1 /*
2  * Unit test suite for the PE loader.
3  *
4  * Copyright 2006,2011 Dmitry Timoshkov
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22 #include <assert.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wine/test.h"
27
28 #define ALIGN_SIZE(size, alignment) (((size) + (alignment - 1)) & ~((alignment - 1)))
29
30 static PVOID RVAToAddr(DWORD_PTR rva, HMODULE module)
31 {
32     if (rva == 0)
33         return NULL;
34     return ((char*) module) + rva;
35 }
36
37 static const struct
38 {
39     WORD e_magic;      /* 00: MZ Header signature */
40     WORD unused[29];
41     DWORD e_lfanew;    /* 3c: Offset to extended header */
42 } dos_header =
43 {
44     IMAGE_DOS_SIGNATURE, { 0 }, sizeof(dos_header)
45 };
46
47 static IMAGE_NT_HEADERS nt_header =
48 {
49     IMAGE_NT_SIGNATURE, /* Signature */
50     {
51 #if defined __i386__
52       IMAGE_FILE_MACHINE_I386, /* Machine */
53 #elif defined __x86_64__
54       IMAGE_FILE_MACHINE_AMD64, /* Machine */
55 #elif defined __powerpc__
56       IMAGE_FILE_MACHINE_POWERPC, /* Machine */
57 #elif defined __sparc__
58       IMAGE_FILE_MACHINE_SPARC, /* Machine */
59 #elif defined __arm__
60       IMAGE_FILE_MACHINE_ARMV7, /* Machine */
61 #else
62 # error You must specify the machine type
63 #endif
64       1, /* NumberOfSections */
65       0, /* TimeDateStamp */
66       0, /* PointerToSymbolTable */
67       0, /* NumberOfSymbols */
68       sizeof(IMAGE_OPTIONAL_HEADER), /* SizeOfOptionalHeader */
69       IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL /* Characteristics */
70     },
71     { IMAGE_NT_OPTIONAL_HDR_MAGIC, /* Magic */
72       1, /* MajorLinkerVersion */
73       0, /* MinorLinkerVersion */
74       0, /* SizeOfCode */
75       0, /* SizeOfInitializedData */
76       0, /* SizeOfUninitializedData */
77       0, /* AddressOfEntryPoint */
78       0x10, /* BaseOfCode, also serves as e_lfanew in the truncated MZ header */
79 #ifndef _WIN64
80       0, /* BaseOfData */
81 #endif
82       0x10000000, /* ImageBase */
83       0, /* SectionAlignment */
84       0, /* FileAlignment */
85       4, /* MajorOperatingSystemVersion */
86       0, /* MinorOperatingSystemVersion */
87       1, /* MajorImageVersion */
88       0, /* MinorImageVersion */
89       4, /* MajorSubsystemVersion */
90       0, /* MinorSubsystemVersion */
91       0, /* Win32VersionValue */
92       sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000, /* SizeOfImage */
93       sizeof(dos_header) + sizeof(nt_header), /* SizeOfHeaders */
94       0, /* CheckSum */
95       IMAGE_SUBSYSTEM_WINDOWS_CUI, /* Subsystem */
96       0, /* DllCharacteristics */
97       0, /* SizeOfStackReserve */
98       0, /* SizeOfStackCommit */
99       0, /* SizeOfHeapReserve */
100       0, /* SizeOfHeapCommit */
101       0, /* LoaderFlags */
102       0, /* NumberOfRvaAndSizes */
103       { { 0 } } /* DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] */
104     }
105 };
106
107 static IMAGE_SECTION_HEADER section =
108 {
109     ".rodata", /* Name */
110     { 0x10 }, /* Misc */
111     0, /* VirtualAddress */
112     0x0a, /* SizeOfRawData */
113     0, /* PointerToRawData */
114     0, /* PointerToRelocations */
115     0, /* PointerToLinenumbers */
116     0, /* NumberOfRelocations */
117     0, /* NumberOfLinenumbers */
118     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ, /* Characteristics */
119 };
120
121 static void test_Loader(void)
122 {
123     static const struct test_data
124     {
125         const void *dos_header;
126         DWORD size_of_dos_header;
127         WORD number_of_sections, size_of_optional_header;
128         DWORD section_alignment, file_alignment;
129         DWORD size_of_image, size_of_headers;
130         DWORD errors[4]; /* 0 means LoadLibrary should succeed */
131     } td[] =
132     {
133         { &dos_header, sizeof(dos_header),
134           1, 0, 0, 0, 0, 0,
135           { ERROR_BAD_EXE_FORMAT }
136         },
137         { &dos_header, sizeof(dos_header),
138           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x1000,
139           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0xe00,
140           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
141           { ERROR_BAD_EXE_FORMAT } /* XP doesn't like too small image size */
142         },
143         { &dos_header, sizeof(dos_header),
144           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x1000,
145           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
146           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
147           { ERROR_SUCCESS }
148         },
149         { &dos_header, sizeof(dos_header),
150           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x1000,
151           0x1f00,
152           0x1000,
153           { ERROR_SUCCESS }
154         },
155         { &dos_header, sizeof(dos_header),
156           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x200, 0x200,
157           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x200,
158           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
159           { ERROR_SUCCESS, ERROR_INVALID_ADDRESS } /* vista is more strict */
160         },
161         { &dos_header, sizeof(dos_header),
162           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x200, 0x1000,
163           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
164           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
165           { ERROR_BAD_EXE_FORMAT } /* XP doesn't like alignments */
166         },
167         { &dos_header, sizeof(dos_header),
168           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x200,
169           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
170           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER),
171           { ERROR_SUCCESS }
172         },
173         { &dos_header, sizeof(dos_header),
174           1, sizeof(IMAGE_OPTIONAL_HEADER), 0x1000, 0x200,
175           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
176           0x200,
177           { ERROR_SUCCESS }
178         },
179         /* Mandatory are all fields up to SizeOfHeaders, everything else
180          * is really optional (at least that's true for XP).
181          */
182         { &dos_header, sizeof(dos_header),
183           1, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
184           sizeof(dos_header) + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum) + sizeof(IMAGE_SECTION_HEADER) + 0x10,
185           sizeof(dos_header) + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum) + sizeof(IMAGE_SECTION_HEADER),
186           { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT, ERROR_INVALID_ADDRESS,
187             ERROR_NOACCESS }
188         },
189         { &dos_header, sizeof(dos_header),
190           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
191           0xd0, /* beyond of the end of file */
192           0xc0, /* beyond of the end of file */
193           { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
194         },
195         { &dos_header, sizeof(dos_header),
196           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
197           0x1000,
198           0,
199           { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
200         },
201         { &dos_header, sizeof(dos_header),
202           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
203           1,
204           0,
205           { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
206         },
207 #if 0 /* not power of 2 alignments need more test cases */
208         { &dos_header, sizeof(dos_header),
209           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x300, 0x300,
210           1,
211           0,
212           { ERROR_BAD_EXE_FORMAT } /* alignment is not power of 2 */
213         },
214 #endif
215         { &dos_header, sizeof(dos_header),
216           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 4, 4,
217           1,
218           0,
219           { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
220         },
221         { &dos_header, sizeof(dos_header),
222           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 1, 1,
223           1,
224           0,
225           { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
226         },
227         { &dos_header, sizeof(dos_header),
228           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum), 0x200, 0x200,
229           0,
230           0,
231           { ERROR_BAD_EXE_FORMAT } /* image size == 0 -> failure */
232         },
233         /* the following data mimics the PE image which upack creates */
234         { &dos_header, 0x10,
235           1, 0x148, 0x1000, 0x200,
236           sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + 0x1000,
237           0x200,
238           { ERROR_SUCCESS }
239         },
240         /* Minimal PE image that XP is able to load: 92 bytes */
241         { &dos_header, 0x04,
242           0, FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, CheckSum),
243           0x04 /* also serves as e_lfanew in the truncated MZ header */, 0x04,
244           1,
245           0,
246           { ERROR_SUCCESS, ERROR_BAD_EXE_FORMAT } /* vista is more strict */
247         }
248     };
249     static const char filler[0x1000];
250     static const char section_data[0x10] = "section data";
251     int i;
252     DWORD dummy, file_size, file_align;
253     HANDLE hfile;
254     HMODULE hlib, hlib_as_data_file;
255     SYSTEM_INFO si;
256     char temp_path[MAX_PATH];
257     char dll_name[MAX_PATH];
258     SIZE_T size;
259     BOOL ret;
260
261     GetSystemInfo(&si);
262     trace("system page size 0x%04x\n", si.dwPageSize);
263
264     /* prevent displaying of the "Unable to load this DLL" message box */
265     SetErrorMode(SEM_FAILCRITICALERRORS);
266
267     GetTempPath(MAX_PATH, temp_path);
268
269     for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
270     {
271         GetTempFileName(temp_path, "ldr", 0, dll_name);
272
273         /*trace("creating %s\n", dll_name);*/
274         hfile = CreateFileA(dll_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
275         if (hfile == INVALID_HANDLE_VALUE)
276         {
277             ok(0, "could not create %s\n", dll_name);
278             break;
279         }
280
281         SetLastError(0xdeadbeef);
282         ret = WriteFile(hfile, td[i].dos_header, td[i].size_of_dos_header, &dummy, NULL);
283         ok(ret, "WriteFile error %d\n", GetLastError());
284
285         nt_header.FileHeader.NumberOfSections = td[i].number_of_sections;
286         nt_header.FileHeader.SizeOfOptionalHeader = td[i].size_of_optional_header;
287
288         nt_header.OptionalHeader.SectionAlignment = td[i].section_alignment;
289         nt_header.OptionalHeader.FileAlignment = td[i].file_alignment;
290         nt_header.OptionalHeader.SizeOfImage = td[i].size_of_image;
291         nt_header.OptionalHeader.SizeOfHeaders = td[i].size_of_headers;
292         SetLastError(0xdeadbeef);
293         ret = WriteFile(hfile, &nt_header, sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER), &dummy, NULL);
294         ok(ret, "WriteFile error %d\n", GetLastError());
295
296         if (nt_header.FileHeader.SizeOfOptionalHeader)
297         {
298             SetLastError(0xdeadbeef);
299             ret = WriteFile(hfile, &nt_header.OptionalHeader,
300                             min(nt_header.FileHeader.SizeOfOptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER)),
301                             &dummy, NULL);
302             ok(ret, "WriteFile error %d\n", GetLastError());
303             if (nt_header.FileHeader.SizeOfOptionalHeader > sizeof(IMAGE_OPTIONAL_HEADER))
304             {
305                 file_align = nt_header.FileHeader.SizeOfOptionalHeader - sizeof(IMAGE_OPTIONAL_HEADER);
306                 assert(file_align < sizeof(filler));
307                 SetLastError(0xdeadbeef);
308                 ret = WriteFile(hfile, filler, file_align, &dummy, NULL);
309                 ok(ret, "WriteFile error %d\n", GetLastError());
310             }
311         }
312
313         assert(nt_header.FileHeader.NumberOfSections <= 1);
314         if (nt_header.FileHeader.NumberOfSections)
315         {
316             if (nt_header.OptionalHeader.SectionAlignment >= si.dwPageSize)
317             {
318                 section.PointerToRawData = td[i].size_of_dos_header;
319                 section.VirtualAddress = nt_header.OptionalHeader.SectionAlignment;
320                 section.Misc.VirtualSize = section.SizeOfRawData * 10;
321             }
322             else
323             {
324                 section.PointerToRawData = nt_header.OptionalHeader.SizeOfHeaders;
325                 section.VirtualAddress = nt_header.OptionalHeader.SizeOfHeaders;
326                 section.Misc.VirtualSize = 5;
327             }
328
329             SetLastError(0xdeadbeef);
330             ret = WriteFile(hfile, &section, sizeof(section), &dummy, NULL);
331             ok(ret, "WriteFile error %d\n", GetLastError());
332
333             /* section data */
334             SetLastError(0xdeadbeef);
335             ret = WriteFile(hfile, section_data, sizeof(section_data), &dummy, NULL);
336             ok(ret, "WriteFile error %d\n", GetLastError());
337         }
338
339         file_size = GetFileSize(hfile, NULL);
340         CloseHandle(hfile);
341
342         SetLastError(0xdeadbeef);
343         hlib = LoadLibrary(dll_name);
344         if (hlib)
345         {
346             MEMORY_BASIC_INFORMATION info;
347
348             ok( td[i].errors[0] == ERROR_SUCCESS, "%d: should have failed\n", i );
349
350             SetLastError(0xdeadbeef);
351             size = VirtualQuery(hlib, &info, sizeof(info));
352             ok(size == sizeof(info),
353                 "%d: VirtualQuery error %d\n", i, GetLastError());
354             ok(info.BaseAddress == hlib, "%d: %p != %p\n", i, info.BaseAddress, hlib);
355             ok(info.AllocationBase == hlib, "%d: %p != %p\n", i, info.AllocationBase, hlib);
356             ok(info.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "%d: %x != PAGE_EXECUTE_WRITECOPY\n", i, info.AllocationProtect);
357             ok(info.RegionSize == ALIGN_SIZE(nt_header.OptionalHeader.SizeOfImage, si.dwPageSize), "%d: got %lx != expected %x\n",
358                i, info.RegionSize, ALIGN_SIZE(nt_header.OptionalHeader.SizeOfImage, si.dwPageSize));
359             ok(info.State == MEM_COMMIT, "%d: %x != MEM_COMMIT\n", i, info.State);
360             if (nt_header.OptionalHeader.SectionAlignment < si.dwPageSize)
361                 ok(info.Protect == PAGE_EXECUTE_WRITECOPY, "%d: %x != PAGE_EXECUTE_WRITECOPY\n", i, info.Protect);
362             else
363                 ok(info.Protect == PAGE_READONLY, "%d: %x != PAGE_READONLY\n", i, info.Protect);
364             ok(info.Type == SEC_IMAGE, "%d: %x != SEC_IMAGE\n", i, info.Type);
365
366             SetLastError(0xdeadbeef);
367             size = VirtualQuery((char *)hlib + info.RegionSize, &info, sizeof(info));
368             ok(size == sizeof(info),
369                 "%d: VirtualQuery error %d\n", i, GetLastError());
370             if (nt_header.OptionalHeader.SectionAlignment == si.dwPageSize ||
371                 nt_header.OptionalHeader.SectionAlignment == nt_header.OptionalHeader.FileAlignment)
372             {
373                 ok(info.BaseAddress == (char *)hlib + ALIGN_SIZE(nt_header.OptionalHeader.SizeOfImage, si.dwPageSize), "%d: got %p != expected %p\n",
374                    i, info.BaseAddress, (char *)hlib + ALIGN_SIZE(nt_header.OptionalHeader.SizeOfImage, si.dwPageSize));
375                 ok(info.AllocationBase == 0, "%d: %p != 0\n", i, info.AllocationBase);
376                 ok(info.AllocationProtect == 0, "%d: %x != 0\n", i, info.AllocationProtect);
377                 /*ok(info.RegionSize == not_practical_value, "%d: %lx != not_practical_value\n", i, info.RegionSize);*/
378                 ok(info.State == MEM_FREE, "%d: %x != MEM_FREE\n", i, info.State);
379                 ok(info.Type == 0, "%d: %x != 0\n", i, info.Type);
380                 ok(info.Protect == PAGE_NOACCESS, "%d: %x != PAGE_NOACCESS\n", i, info.Protect);
381             }
382             else
383             {
384                 ok(info.Protect == PAGE_EXECUTE_WRITECOPY, "%d: %x != PAGE_EXECUTE_WRITECOPY\n", i, info.Protect);
385                 ok(info.BaseAddress == hlib, "%d: got %p != expected %p\n", i, info.BaseAddress, hlib);
386                 ok(info.AllocationBase == hlib, "%d: %p != %p\n", i, info.AllocationBase, hlib);
387                 ok(info.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "%d: %x != PAGE_EXECUTE_WRITECOPY\n", i, info.AllocationProtect);
388                 ok(info.RegionSize == ALIGN_SIZE(file_size, si.dwPageSize), "%d: got %lx != expected %x\n",
389                    i, info.RegionSize, ALIGN_SIZE(file_size, si.dwPageSize));
390                 ok(info.State == MEM_COMMIT, "%d: %x != MEM_COMMIT\n", i, info.State);
391                 ok(info.Protect == PAGE_READONLY, "%d: %x != PAGE_READONLY\n", i, info.Protect);
392                 ok(info.Type == SEC_IMAGE, "%d: %x != SEC_IMAGE\n", i, info.Type);
393             }
394
395             /* header: check the zeroing of alignment */
396             if (nt_header.OptionalHeader.SectionAlignment >= si.dwPageSize)
397             {
398                 const char *start;
399                 int size;
400
401                 start = (const char *)hlib + nt_header.OptionalHeader.SizeOfHeaders;
402                 size = ALIGN_SIZE((ULONG_PTR)start, si.dwPageSize) - (ULONG_PTR)start;
403                 ok(!memcmp(start, filler, size), "%d: header alignment is not cleared\n", i);
404             }
405
406             if (nt_header.FileHeader.NumberOfSections)
407             {
408                 SetLastError(0xdeadbeef);
409                 size = VirtualQuery((char *)hlib + section.VirtualAddress, &info, sizeof(info));
410                 ok(size == sizeof(info),
411                     "%d: VirtualQuery error %d\n", i, GetLastError());
412                 if (nt_header.OptionalHeader.SectionAlignment < si.dwPageSize)
413                 {
414                     ok(info.BaseAddress == hlib, "%d: got %p != expected %p\n", i, info.BaseAddress, hlib);
415                     ok(info.RegionSize == ALIGN_SIZE(nt_header.OptionalHeader.SizeOfImage, si.dwPageSize), "%d: got %lx != expected %x\n",
416                        i, info.RegionSize, ALIGN_SIZE(nt_header.OptionalHeader.SizeOfImage, si.dwPageSize));
417                     ok(info.Protect == PAGE_EXECUTE_WRITECOPY, "%d: %x != PAGE_EXECUTE_WRITECOPY\n", i, info.Protect);
418                 }
419                 else
420                 {
421                     ok(info.BaseAddress == (char *)hlib + section.VirtualAddress, "%d: got %p != expected %p\n", i, info.BaseAddress, (char *)hlib + section.VirtualAddress);
422                     ok(info.RegionSize == ALIGN_SIZE(section.Misc.VirtualSize, si.dwPageSize), "%d: got %lx != expected %x\n",
423                        i, info.RegionSize, ALIGN_SIZE(section.Misc.VirtualSize, si.dwPageSize));
424                     ok(info.Protect == PAGE_READONLY, "%d: %x != PAGE_READONLY\n", i, info.Protect);
425                 }
426                 ok(info.AllocationBase == hlib, "%d: %p != %p\n", i, info.AllocationBase, hlib);
427                 ok(info.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "%d: %x != PAGE_EXECUTE_WRITECOPY\n", i, info.AllocationProtect);
428                 ok(info.State == MEM_COMMIT, "%d: %x != MEM_COMMIT\n", i, info.State);
429                 ok(info.Type == SEC_IMAGE, "%d: %x != SEC_IMAGE\n", i, info.Type);
430
431                 if (nt_header.OptionalHeader.SectionAlignment >= si.dwPageSize)
432                     ok(!memcmp((const char *)hlib + section.VirtualAddress + section.PointerToRawData, &nt_header, section.SizeOfRawData), "wrong section data\n");
433                 else
434                     ok(!memcmp((const char *)hlib + section.PointerToRawData, section_data, section.SizeOfRawData), "wrong section data\n");
435
436                 /* check the zeroing of alignment */
437                 if (nt_header.OptionalHeader.SectionAlignment >= si.dwPageSize)
438                 {
439                     const char *start;
440                     int size;
441
442                     start = (const char *)hlib + section.VirtualAddress + section.PointerToRawData + section.SizeOfRawData;
443                     size = ALIGN_SIZE((ULONG_PTR)start, si.dwPageSize) - (ULONG_PTR)start;
444                     ok(memcmp(start, filler, size), "%d: alignment should not be cleared\n", i);
445                 }
446             }
447
448             SetLastError(0xdeadbeef);
449             hlib_as_data_file = LoadLibraryEx(dll_name, 0, LOAD_LIBRARY_AS_DATAFILE);
450             ok(hlib_as_data_file != 0, "LoadLibraryEx error %u\n", GetLastError());
451             ok(hlib_as_data_file == hlib, "hlib_as_file and hlib are different\n");
452
453             SetLastError(0xdeadbeef);
454             ret = FreeLibrary(hlib);
455             ok(ret, "FreeLibrary error %d\n", GetLastError());
456
457             SetLastError(0xdeadbeef);
458             hlib = GetModuleHandle(dll_name);
459             ok(hlib != 0, "GetModuleHandle error %u\n", GetLastError());
460
461             SetLastError(0xdeadbeef);
462             ret = FreeLibrary(hlib_as_data_file);
463             ok(ret, "FreeLibrary error %d\n", GetLastError());
464
465             hlib = GetModuleHandle(dll_name);
466             ok(!hlib, "GetModuleHandle should fail\n");
467
468             SetLastError(0xdeadbeef);
469             hlib_as_data_file = LoadLibraryEx(dll_name, 0, LOAD_LIBRARY_AS_DATAFILE);
470             ok(hlib_as_data_file != 0, "LoadLibraryEx error %u\n", GetLastError());
471             ok((ULONG_PTR)hlib_as_data_file & 1, "hlib_as_data_file is even\n");
472
473             hlib = GetModuleHandle(dll_name);
474             ok(!hlib, "GetModuleHandle should fail\n");
475
476             SetLastError(0xdeadbeef);
477             ret = FreeLibrary(hlib_as_data_file);
478             ok(ret, "FreeLibrary error %d\n", GetLastError());
479         }
480         else
481         {
482             BOOL error_match;
483             int error_index;
484
485             error_match = FALSE;
486             for (error_index = 0;
487                  ! error_match && error_index < sizeof(td[i].errors) / sizeof(DWORD);
488                  error_index++)
489             {
490                 error_match = td[i].errors[error_index] == GetLastError();
491             }
492             ok(error_match, "%d: unexpected error %d\n", i, GetLastError());
493         }
494
495         SetLastError(0xdeadbeef);
496         ret = DeleteFile(dll_name);
497         ok(ret, "DeleteFile error %d\n", GetLastError());
498     }
499 }
500
501 /* Verify linking style of import descriptors */
502 static void test_ImportDescriptors(void)
503 {
504     HMODULE kernel32_module = NULL;
505     PIMAGE_DOS_HEADER d_header;
506     PIMAGE_NT_HEADERS nt_headers;
507     DWORD import_dir_size;
508     DWORD_PTR dir_offset;
509     PIMAGE_IMPORT_DESCRIPTOR import_chunk;
510
511     /* Load kernel32 module */
512     kernel32_module = GetModuleHandleA("kernel32.dll");
513     assert( kernel32_module != NULL );
514
515     /* Get PE header info from module image */
516     d_header = (PIMAGE_DOS_HEADER) kernel32_module;
517     nt_headers = (PIMAGE_NT_HEADERS) (((char*) d_header) +
518             d_header->e_lfanew);
519
520     /* Get size of import entry directory */
521     import_dir_size = nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
522     if (!import_dir_size)
523     {
524         skip("Unable to continue testing due to missing import directory.\n");
525         return;
526     }
527
528     /* Get address of first import chunk */
529     dir_offset = nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
530     import_chunk = RVAToAddr(dir_offset, kernel32_module);
531     ok(import_chunk != 0, "Invalid import_chunk: %p\n", import_chunk);
532     if (!import_chunk) return;
533
534     /* Iterate through import descriptors and verify set name,
535      * OriginalFirstThunk, and FirstThunk.  Core Windows DLLs, such as
536      * kernel32.dll, don't use Borland-style linking, where the table of
537      * imported names is stored directly in FirstThunk and overwritten
538      * by the relocation, instead of being stored in OriginalFirstThunk.
539      * */
540     for (; import_chunk->FirstThunk; import_chunk++)
541     {
542         LPCSTR module_name = RVAToAddr(import_chunk->Name, kernel32_module);
543         PIMAGE_THUNK_DATA name_table = RVAToAddr(
544                 U(*import_chunk).OriginalFirstThunk, kernel32_module);
545         PIMAGE_THUNK_DATA iat = RVAToAddr(
546                 import_chunk->FirstThunk, kernel32_module);
547         ok(module_name != NULL, "Imported module name should not be NULL\n");
548         ok(name_table != NULL,
549                 "Name table for imported module %s should not be NULL\n",
550                 module_name);
551         ok(iat != NULL, "IAT for imported module %s should not be NULL\n",
552                 module_name);
553     }
554 }
555
556 static BOOL is_mem_writable(DWORD prot)
557 {
558     switch (prot & 0xff)
559     {
560         case PAGE_READWRITE:
561         case PAGE_WRITECOPY:
562         case PAGE_EXECUTE_READWRITE:
563         case PAGE_EXECUTE_WRITECOPY:
564             return TRUE;
565
566         default:
567             return FALSE;
568     }
569 }
570
571 static void test_section_access(void)
572 {
573     static const struct test_data
574     {
575         DWORD scn_file_access, scn_page_access, scn_page_access_after_write;
576     } td[] =
577     {
578         { 0, PAGE_NOACCESS, 0 },
579         { IMAGE_SCN_MEM_READ, PAGE_READONLY, 0 },
580         { IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY, PAGE_READWRITE },
581         { IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE, 0 },
582         { IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY, PAGE_READWRITE },
583         { IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_READ },
584         { IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READWRITE },
585         { IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READWRITE },
586
587         { IMAGE_SCN_CNT_INITIALIZED_DATA, PAGE_NOACCESS, 0 },
588         { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ, PAGE_READONLY, 0 },
589         { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY, PAGE_READWRITE },
590         { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE, 0 },
591         { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY, PAGE_READWRITE },
592         { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_READ, 0 },
593         { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READWRITE },
594         { IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READWRITE },
595
596         { IMAGE_SCN_CNT_UNINITIALIZED_DATA, PAGE_NOACCESS, 0 },
597         { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ, PAGE_READONLY, 0 },
598         { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY, PAGE_READWRITE },
599         { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE, 0 },
600         { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE, PAGE_WRITECOPY, PAGE_READWRITE },
601         { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_READ, 0 },
602         { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READWRITE },
603         { IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, PAGE_EXECUTE_WRITECOPY, PAGE_EXECUTE_READWRITE }
604     };
605     static const char filler[0x1000];
606     static const char section_data[0x10] = "section data";
607     int i;
608     DWORD dummy, file_align;
609     HANDLE hfile;
610     HMODULE hlib;
611     SYSTEM_INFO si;
612     char temp_path[MAX_PATH];
613     char dll_name[MAX_PATH];
614     SIZE_T size;
615     MEMORY_BASIC_INFORMATION info;
616     BOOL ret;
617
618     GetSystemInfo(&si);
619     trace("system page size %#x\n", si.dwPageSize);
620
621     /* prevent displaying of the "Unable to load this DLL" message box */
622     SetErrorMode(SEM_FAILCRITICALERRORS);
623
624     GetTempPath(MAX_PATH, temp_path);
625
626     for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
627     {
628         GetTempFileName(temp_path, "ldr", 0, dll_name);
629
630         /*trace("creating %s\n", dll_name);*/
631         hfile = CreateFileA(dll_name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
632         if (hfile == INVALID_HANDLE_VALUE)
633         {
634             ok(0, "could not create %s\n", dll_name);
635             return;
636         }
637
638         SetLastError(0xdeadbeef);
639         ret = WriteFile(hfile, &dos_header, sizeof(dos_header), &dummy, NULL);
640         ok(ret, "WriteFile error %d\n", GetLastError());
641
642         nt_header.FileHeader.NumberOfSections = 1;
643         nt_header.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER);
644
645         nt_header.OptionalHeader.SectionAlignment = si.dwPageSize;
646         nt_header.OptionalHeader.FileAlignment = 0x200;
647         nt_header.OptionalHeader.SizeOfImage = sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER) + si.dwPageSize;
648         nt_header.OptionalHeader.SizeOfHeaders = sizeof(dos_header) + sizeof(nt_header) + sizeof(IMAGE_SECTION_HEADER);
649         SetLastError(0xdeadbeef);
650         ret = WriteFile(hfile, &nt_header, sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER), &dummy, NULL);
651         ok(ret, "WriteFile error %d\n", GetLastError());
652         SetLastError(0xdeadbeef);
653         ret = WriteFile(hfile, &nt_header.OptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER), &dummy, NULL);
654         ok(ret, "WriteFile error %d\n", GetLastError());
655
656         section.SizeOfRawData = sizeof(section_data);
657         section.PointerToRawData = nt_header.OptionalHeader.FileAlignment;
658         section.VirtualAddress = nt_header.OptionalHeader.SectionAlignment;
659         section.Misc.VirtualSize = section.SizeOfRawData;
660         section.Characteristics = td[i].scn_file_access;
661         SetLastError(0xdeadbeef);
662         ret = WriteFile(hfile, &section, sizeof(section), &dummy, NULL);
663         ok(ret, "WriteFile error %d\n", GetLastError());
664
665         file_align = nt_header.OptionalHeader.FileAlignment - nt_header.OptionalHeader.SizeOfHeaders;
666         assert(file_align < sizeof(filler));
667         SetLastError(0xdeadbeef);
668         ret = WriteFile(hfile, filler, file_align, &dummy, NULL);
669         ok(ret, "WriteFile error %d\n", GetLastError());
670
671         /* section data */
672         SetLastError(0xdeadbeef);
673         ret = WriteFile(hfile, section_data, sizeof(section_data), &dummy, NULL);
674         ok(ret, "WriteFile error %d\n", GetLastError());
675
676         CloseHandle(hfile);
677
678         SetLastError(0xdeadbeef);
679         hlib = LoadLibrary(dll_name);
680         ok(hlib != 0, "LoadLibrary error %d\n", GetLastError());
681
682         SetLastError(0xdeadbeef);
683         size = VirtualQuery((char *)hlib + section.VirtualAddress, &info, sizeof(info));
684         ok(size == sizeof(info),
685             "%d: VirtualQuery error %d\n", i, GetLastError());
686         ok(info.BaseAddress == (char *)hlib + section.VirtualAddress, "%d: got %p != expected %p\n", i, info.BaseAddress, (char *)hlib + section.VirtualAddress);
687         ok(info.RegionSize == si.dwPageSize, "%d: got %#lx != expected %#x\n", i, info.RegionSize, si.dwPageSize);
688         /* FIXME: remove the condition below once Wine is fixed */
689         if ((td[i].scn_file_access & IMAGE_SCN_MEM_WRITE) &&
690             (td[i].scn_file_access & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
691         todo_wine ok(info.Protect == td[i].scn_page_access, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].scn_page_access);
692         else
693         ok(info.Protect == td[i].scn_page_access, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].scn_page_access);
694         ok(info.AllocationBase == hlib, "%d: %p != %p\n", i, info.AllocationBase, hlib);
695         ok(info.AllocationProtect == PAGE_EXECUTE_WRITECOPY, "%d: %#x != PAGE_EXECUTE_WRITECOPY\n", i, info.AllocationProtect);
696         ok(info.State == MEM_COMMIT, "%d: %#x != MEM_COMMIT\n", i, info.State);
697         ok(info.Type == SEC_IMAGE, "%d: %#x != SEC_IMAGE\n", i, info.Type);
698         if (info.Protect != PAGE_NOACCESS)
699             ok(!memcmp((const char *)info.BaseAddress, section_data, section.SizeOfRawData), "wrong section data\n");
700
701         /* Windows changes the WRITECOPY to WRITE protection on an image section write (for a changed page only) */
702         if (is_mem_writable(info.Protect))
703         {
704             char *p = info.BaseAddress;
705             *p = 0xfe;
706             SetLastError(0xdeadbeef);
707             size = VirtualQuery((char *)hlib + section.VirtualAddress, &info, sizeof(info));
708             ok(size == sizeof(info), "%d: VirtualQuery error %d\n", i, GetLastError());
709             /* FIXME: remove the condition below once Wine is fixed */
710             if (info.Protect == PAGE_WRITECOPY || info.Protect == PAGE_EXECUTE_WRITECOPY)
711                 todo_wine ok(info.Protect == td[i].scn_page_access_after_write, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].scn_page_access_after_write);
712             else
713                 ok(info.Protect == td[i].scn_page_access_after_write, "%d: got %#x != expected %#x\n", i, info.Protect, td[i].scn_page_access_after_write);
714         }
715
716         SetLastError(0xdeadbeef);
717         ret = FreeLibrary(hlib);
718         ok(ret, "FreeLibrary error %d\n", GetLastError());
719
720         SetLastError(0xdeadbeef);
721         ret = DeleteFile(dll_name);
722         ok(ret, "DeleteFile error %d\n", GetLastError());
723     }
724 }
725
726 START_TEST(loader)
727 {
728     test_Loader();
729     test_ImportDescriptors();
730     test_section_access();
731 }