1 /*
2  * Unit test suite for resource functions.
3  *
4  * Copyright 2006 Mike McCormack
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 "precomp.h"
22 
23 static const char filename[] = "test_.exe";
24 static DWORD GLE;
25 
26 enum constants {
27     page_size = 0x1000,
28     rva_rsrc_start = page_size * 3,
29     max_sections = 3
30 };
31 
32 /* rodata @ [0x1000-0x3000) */
33 static const IMAGE_SECTION_HEADER sh_rodata_1 =
34 {
35     ".rodata", {2*page_size}, page_size, 2*page_size, page_size, 0, 0, 0, 0,
36     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
37 };
38 
39 /* rodata @ [0x1000-0x2000) */
40 static const IMAGE_SECTION_HEADER sh_rodata_2 =
41 {
42     ".rodata", {page_size}, page_size, page_size, page_size, 0, 0, 0, 0,
43     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
44 };
45 
46 /* rsrc @ [0x3000-0x4000) */
47 static const IMAGE_SECTION_HEADER sh_rsrc_1 =
48 {
49     ".rsrc\0\0", {page_size}, rva_rsrc_start, page_size, rva_rsrc_start, 0, 0, 0, 0,
50     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
51 };
52 
53 /* rsrc @ [0x2000-0x4000) */
54 static const IMAGE_SECTION_HEADER sh_rsrc_2 =
55 {
56     ".rsrc\0\0", {2*page_size}, rva_rsrc_start-page_size, 2*page_size, rva_rsrc_start-page_size, 0, 0, 0, 0,
57     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
58 };
59 
60 /* rsrc @ [0x2000-0x3000) */
61 static const IMAGE_SECTION_HEADER sh_rsrc_3 =
62 {
63     ".rsrc\0\0", {page_size}, rva_rsrc_start-page_size, page_size, rva_rsrc_start-page_size, 0, 0, 0, 0,
64     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
65 };
66 
67 /* rsrc @ [0x3000-0x6000) */
68 static const IMAGE_SECTION_HEADER sh_rsrc_4 =
69 {
70     ".rsrc\0\0", {3*page_size}, rva_rsrc_start, 3*page_size, rva_rsrc_start, 0, 0, 0, 0,
71     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
72 };
73 
74 /* rsrc @ [0x2000-0x5000) */
75 static const IMAGE_SECTION_HEADER sh_rsrc_5 =
76 {
77     ".rsrc\0\0", {3*page_size}, 2*page_size, 3*page_size, 2*page_size, 0, 0, 0, 0,
78     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
79 };
80 
81 /* rsrc @ [0x3000-0x4000), small SizeOfRawData */
82 static const IMAGE_SECTION_HEADER sh_rsrc_6 =
83 {
84     ".rsrc\0\0", {page_size}, rva_rsrc_start, 8, rva_rsrc_start, 0, 0, 0, 0,
85     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
86 };
87 
88 /* reloc @ [0x4000-0x5000) */
89 static const IMAGE_SECTION_HEADER sh_junk =
90 {
91     ".reloc\0", {page_size}, 4*page_size, page_size, 4*page_size, 0, 0, 0, 0,
92     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
93 };
94 
95 /* reloc @ [0x6000-0x7000) */
96 static const IMAGE_SECTION_HEADER sh_junk_2 =
97 {
98     ".reloc\0", {page_size}, 6*page_size, page_size, 6*page_size, 0, 0, 0, 0,
99     IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
100 };
101 
102 typedef struct _sec_build
103 {
104     const IMAGE_SECTION_HEADER *sect_in[max_sections];
105 } sec_build;
106 
107 typedef struct _sec_verify
108 {
109     const IMAGE_SECTION_HEADER *sect_out[max_sections];
110     DWORD length;
111     int rsrc_section;
112     DWORD NumberOfNamedEntries, NumberOfIdEntries;
113 } sec_verify;
114 
115 static const struct _sec_variants
116 {
117     sec_build build;
118     sec_verify chk_none, chk_delete, chk_version, chk_bigdata;
119 } sec_variants[] =
120 {
121     /* .rsrc is the last section, data directory entry points to whole section */
122     {
123         {{&sh_rodata_1, &sh_rsrc_1, NULL}},
124         {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0},
125         {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0},
126         {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 1},
127         {{&sh_rodata_1, &sh_rsrc_4, NULL}, 6*page_size, 1, 0, 1}
128     },
129     /* .rsrc is the last section, data directory entry points to section end */
130     /* Vista+ - resources are moved to section start (trashing data that could be there), and section is trimmed */
131     /* NT4/2000/2003 - resources are moved to section start (trashing data that could be there); section isn't trimmed */
132     {
133         {{&sh_rodata_2, &sh_rsrc_2, NULL}},
134         {{&sh_rodata_2, &sh_rsrc_3, NULL}, 3*page_size, 1, 0, 0},
135         {{&sh_rodata_2, &sh_rsrc_3, NULL}, 3*page_size, 1, 0, 0},
136         {{&sh_rodata_2, &sh_rsrc_3, NULL}, 3*page_size, 1, 0, 1},
137         {{&sh_rodata_2, &sh_rsrc_5, NULL}, 5*page_size, 1, 0, 1}
138     },
139     /* .rsrc is not the last section */
140     /* section is reused; sections after .rsrc are shifted to give space to rsrc (in-image offset and RVA!) */
141     {
142         {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}},
143         {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}, 5*page_size, 1, 0, 0},
144         {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}, 5*page_size, 1, 0, 0},
145         {{&sh_rodata_1, &sh_rsrc_1, &sh_junk}, 5*page_size, 1, 0, 1},
146         {{&sh_rodata_1, &sh_rsrc_4, &sh_junk_2}, 7*page_size, 1, 0, 1}
147     },
148     /* .rsrc is the last section, data directory entry points to whole section, file size is not aligned on FileAlign */
149     {
150         {{&sh_rodata_1, &sh_rsrc_6, NULL}},
151         {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0},
152         {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 0},
153         {{&sh_rodata_1, &sh_rsrc_1, NULL}, 4*page_size, 1, 0, 1},
154         {{&sh_rodata_1, &sh_rsrc_4, NULL}, 6*page_size, 1, 0, 1}
155     }
156 };
157 
158 static int build_exe( const sec_build* sec_descr )
159 {
160     IMAGE_DOS_HEADER *dos;
161     IMAGE_NT_HEADERS *nt;
162     IMAGE_SECTION_HEADER *sec;
163     IMAGE_OPTIONAL_HEADER *opt;
164     HANDLE file;
165     DWORD written, i, file_size;
166     BYTE page[page_size];
167 
168     memset( page, 0, sizeof page );
169 
170     dos = (void*) page;
171     dos->e_magic = IMAGE_DOS_SIGNATURE;
172     dos->e_lfanew = sizeof *dos;
173 
174     nt = (void*) &dos[1];
175 
176     nt->Signature = IMAGE_NT_SIGNATURE;
177     nt->FileHeader.Machine = IMAGE_FILE_MACHINE_I386;
178     nt->FileHeader.NumberOfSections = 0;
179     nt->FileHeader.SizeOfOptionalHeader = sizeof nt->OptionalHeader;
180     nt->FileHeader.Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL;
181 
182     opt = &nt->OptionalHeader;
183 
184     opt->Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC;
185     opt->MajorLinkerVersion = 1;
186     opt->BaseOfCode = 0x10;
187     opt->ImageBase = 0x10000000;
188     opt->MajorOperatingSystemVersion = 4;
189     opt->MajorImageVersion = 1;
190     opt->MajorSubsystemVersion = 4;
191     opt->SizeOfHeaders = sizeof *dos + sizeof *nt + sizeof *sec * 2;
192     opt->SizeOfImage = page_size;
193     opt->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
194 
195     /* if SectionAlignment and File alignment are not specified */
196     /* UpdateResource fails trying to create a huge temporary file */
197     opt->SectionAlignment = page_size;
198     opt->FileAlignment = page_size;
199 
200     opt->DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY].VirtualAddress = rva_rsrc_start;
201     opt->DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY].Size = page_size;
202 
203     sec = (void*) &nt[1];
204 
205     file_size = 0;
206     for ( i = 0; i < max_sections; i++ )
207         if ( sec_descr->sect_in[i] )
208         {
209             DWORD virt_end_of_section = sec_descr->sect_in[i]->Misc.VirtualSize +
210                 sec_descr->sect_in[i]->VirtualAddress;
211             DWORD phys_end_of_section = sec_descr->sect_in[i]->SizeOfRawData +
212                 sec_descr->sect_in[i]->PointerToRawData;
213             memcpy( sec + nt->FileHeader.NumberOfSections, sec_descr->sect_in[i],
214                     sizeof(sec[0]) );
215             nt->FileHeader.NumberOfSections++;
216             if ( opt->SizeOfImage < virt_end_of_section )
217                 opt->SizeOfImage = virt_end_of_section;
218             if ( file_size < phys_end_of_section )
219                 file_size = phys_end_of_section;
220         }
221 
222     file = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
223     ok (file != INVALID_HANDLE_VALUE, "failed to create file\n");
224 
225     /* write out the header */
226     WriteFile( file, page, sizeof page, &written, NULL );
227 
228     /* write out zeroed pages for sections */
229     memset( page, 0, sizeof page );
230     for ( i = page_size; i < file_size; i += page_size )
231     {
232 	DWORD size = min(page_size, file_size - i);
233         WriteFile( file, page, size, &written, NULL );
234     }
235 
236     CloseHandle( file );
237 
238     return 0;
239 }
240 
241 static void update_missing_exe( void )
242 {
243     HANDLE res;
244 
245     SetLastError(0xdeadbeef);
246     res = BeginUpdateResourceA( filename, TRUE );
247     GLE = GetLastError();
248     ok( res == NULL, "BeginUpdateResource should fail\n");
249 }
250 
251 static void update_empty_exe( void )
252 {
253     HANDLE file, res, test;
254     BOOL r;
255 
256     file = CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
257     ok (file != INVALID_HANDLE_VALUE, "failed to create file\n");
258 
259     CloseHandle( file );
260 
261     res = BeginUpdateResourceA( filename, TRUE );
262     if ( res != NULL || GetLastError() != ERROR_FILE_INVALID )
263     {
264         ok( res != NULL, "BeginUpdateResource failed\n");
265 
266         /* check if it's possible to open the file now */
267         test = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
268         ok (test != INVALID_HANDLE_VALUE, "failed to create file\n");
269 
270         CloseHandle( test );
271 
272         r = EndUpdateResourceA( res, FALSE );
273         ok( r == FALSE, "EndUpdateResource failed\n");
274     }
275     else
276         skip( "Can't update resource in empty file\n" );
277 
278     res = BeginUpdateResourceA( filename, FALSE );
279     ok( res == NULL, "BeginUpdateResource failed\n");
280 }
281 
282 static void update_resources_none( void )
283 {
284     HMODULE res;
285     BOOL r;
286 
287     res = BeginUpdateResourceA( filename, FALSE );
288     ok( res != NULL, "BeginUpdateResource failed\n");
289 
290     r = EndUpdateResourceA( res, FALSE );
291     ok( r, "EndUpdateResource failed\n");
292 }
293 
294 static void update_resources_delete( void )
295 {
296     HMODULE res;
297     BOOL r;
298 
299     res = BeginUpdateResourceA( filename, TRUE );
300     ok( res != NULL, "BeginUpdateResource failed\n");
301 
302     r = EndUpdateResourceA( res, FALSE );
303     ok( r, "EndUpdateResource failed\n");
304 }
305 
306 static void update_resources_version( void )
307 {
308     HANDLE res = NULL;
309     BOOL r;
310     char foo[] = "red and white";
311 
312     res = BeginUpdateResourceA( filename, TRUE );
313     ok( res != NULL, "BeginUpdateResource failed\n");
314 
315     if (0)  /* this causes subsequent tests to fail on Vista */
316     {
317         r = UpdateResourceA( res,
318                             MAKEINTRESOURCEA(0x1230),
319                             MAKEINTRESOURCEA(0x4567),
320                             0xabcd,
321                             NULL, 0 );
322         ok( r == FALSE, "UpdateResource failed\n");
323     }
324 
325     r = UpdateResourceA( res,
326                         MAKEINTRESOURCEA(0x1230),
327                         MAKEINTRESOURCEA(0x4567),
328                         0xabcd,
329                         foo, sizeof foo );
330     ok( r == TRUE, "UpdateResource failed: %d\n", GetLastError());
331 
332     r = EndUpdateResourceA( res, FALSE );
333     ok( r, "EndUpdateResource failed: %d\n", GetLastError());
334 }
335 
336 static void update_resources_bigdata( void )
337 {
338     HANDLE res = NULL;
339     BOOL r;
340     char foo[2*page_size] = "foobar";
341 
342     res = BeginUpdateResourceA( filename, TRUE );
343     ok( res != NULL, "BeginUpdateResource succeeded\n");
344 
345     r = UpdateResourceA( res,
346                         MAKEINTRESOURCEA(0x3012),
347                         MAKEINTRESOURCEA(0x5647),
348                         0xcdba,
349                         foo, sizeof foo );
350     ok( r == TRUE, "UpdateResource failed: %d\n", GetLastError());
351 
352     r = EndUpdateResourceA( res, FALSE );
353     ok( r, "EndUpdateResource failed\n");
354 }
355 
356 static void check_exe( const sec_verify *verify )
357 {
358     int i;
359     IMAGE_DOS_HEADER *dos;
360     IMAGE_NT_HEADERS *nt;
361     IMAGE_OPTIONAL_HEADER *opt;
362     IMAGE_SECTION_HEADER *sec;
363     IMAGE_RESOURCE_DIRECTORY *dir;
364     HANDLE file, mapping;
365     DWORD length, sec_count = 0;
366 
367     file = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
368     ok (file != INVALID_HANDLE_VALUE, "failed to create file (%d)\n", GetLastError());
369 
370     length = GetFileSize( file, NULL );
371     ok( length >= verify->length, "file size wrong\n");
372 
373     mapping = CreateFileMappingA( file, NULL, PAGE_READONLY, 0, 0, NULL );
374     ok (mapping != NULL, "failed to create file\n");
375 
376     dos = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, length );
377     ok( dos != NULL, "failed to map file\n");
378 
379     if (!dos)
380         goto end;
381 
382     nt = (void*) ((BYTE*) dos + dos->e_lfanew);
383     opt = &nt->OptionalHeader;
384     sec = (void*) &nt[1];
385 
386     for(i = 0; i < max_sections; i++)
387         if (verify->sect_out[i])
388         {
389             ok( !memcmp(&verify->sect_out[i]->Name, &sec[sec_count].Name, 8), "section %d name wrong\n", sec_count);
390             ok( verify->sect_out[i]->VirtualAddress == sec[sec_count].VirtualAddress, "section %d vaddr wrong\n", sec_count);
391             ok( verify->sect_out[i]->SizeOfRawData <= sec[sec_count].SizeOfRawData, "section %d SizeOfRawData wrong (%d vs %d)\n", sec_count, verify->sect_out[i]->SizeOfRawData ,sec[sec_count].SizeOfRawData);
392             ok( verify->sect_out[i]->PointerToRawData == sec[sec_count].PointerToRawData, "section %d PointerToRawData wrong\n", sec_count);
393             ok( verify->sect_out[i]->Characteristics == sec[sec_count].Characteristics , "section %d characteristics wrong\n", sec_count);
394             sec_count++;
395         }
396 
397     ok( nt->FileHeader.NumberOfSections == sec_count, "number of sections wrong\n" );
398 
399     if (verify->rsrc_section >= 0 && verify->rsrc_section < nt->FileHeader.NumberOfSections)
400     {
401         dir = (void*) ((BYTE*) dos + sec[verify->rsrc_section].VirtualAddress);
402 
403         ok( dir->Characteristics == 0, "Characteristics wrong\n");
404         ok( dir->TimeDateStamp == 0 || abs( dir->TimeDateStamp - GetTickCount() ) < 1000 /* nt4 */,
405             "TimeDateStamp wrong %u\n", dir->TimeDateStamp);
406         ok( dir->MajorVersion == 4, "MajorVersion wrong\n");
407         ok( dir->MinorVersion == 0, "MinorVersion wrong\n");
408 
409         ok( dir->NumberOfNamedEntries == verify->NumberOfNamedEntries, "NumberOfNamedEntries should be %d instead of %d\n",
410                 verify->NumberOfNamedEntries, dir->NumberOfNamedEntries);
411         ok( dir->NumberOfIdEntries == verify->NumberOfIdEntries, "NumberOfIdEntries should be %d instead of %d\n",
412                 verify->NumberOfIdEntries, dir->NumberOfIdEntries);
413 
414         ok(opt->DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY].VirtualAddress == sec[verify->rsrc_section].VirtualAddress,
415                 "VirtualAddress in optional header should be %d instead of %d\n",
416                 sec[verify->rsrc_section].VirtualAddress, opt->DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY].VirtualAddress);
417     }
418 
419 end:
420     UnmapViewOfFile( dos );
421 
422     CloseHandle( mapping );
423 
424     CloseHandle( file );
425 }
426 
427 static void test_find_resource(void)
428 {
429     HRSRC rsrc;
430 
431     rsrc = FindResourceW( GetModuleHandleW(NULL), MAKEINTRESOURCEW(1), (LPCWSTR)RT_MENU );
432     ok( rsrc != 0, "resource not found\n" );
433     rsrc = FindResourceExW( GetModuleHandleW(NULL), (LPCWSTR)RT_MENU, MAKEINTRESOURCEW(1),
434                             MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ));
435     ok( rsrc != 0, "resource not found\n" );
436     rsrc = FindResourceExW( GetModuleHandleW(NULL), (LPCWSTR)RT_MENU, MAKEINTRESOURCEW(1),
437                             MAKELANGID( LANG_GERMAN, SUBLANG_DEFAULT ));
438     ok( rsrc != 0, "resource not found\n" );
439 
440     SetLastError( 0xdeadbeef );
441     rsrc = FindResourceW( GetModuleHandleW(NULL), MAKEINTRESOURCEW(1), (LPCWSTR)RT_DIALOG );
442     ok( !rsrc, "resource found\n" );
443     ok( GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND, "wrong error %u\n", GetLastError() );
444 
445     SetLastError( 0xdeadbeef );
446     rsrc = FindResourceW( GetModuleHandleW(NULL), MAKEINTRESOURCEW(2), (LPCWSTR)RT_MENU );
447     ok( !rsrc, "resource found\n" );
448     ok( GetLastError() == ERROR_RESOURCE_NAME_NOT_FOUND, "wrong error %u\n", GetLastError() );
449 
450     SetLastError( 0xdeadbeef );
451     rsrc = FindResourceExW( GetModuleHandleW(NULL), (LPCWSTR)RT_MENU, MAKEINTRESOURCEW(1),
452                             MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ) );
453     ok( !rsrc, "resource found\n" );
454     ok( GetLastError() == ERROR_RESOURCE_LANG_NOT_FOUND, "wrong error %u\n", GetLastError() );
455 
456     SetLastError( 0xdeadbeef );
457     rsrc = FindResourceExW( GetModuleHandleW(NULL), (LPCWSTR)RT_MENU, MAKEINTRESOURCEW(1),
458                             MAKELANGID( LANG_FRENCH, SUBLANG_DEFAULT ) );
459     ok( !rsrc, "resource found\n" );
460     ok( GetLastError() == ERROR_RESOURCE_LANG_NOT_FOUND, "wrong error %u\n", GetLastError() );
461 }
462 
463 START_TEST(resource)
464 {
465     DWORD i;
466 
467     DeleteFileA( filename );
468     update_missing_exe();
469 
470     if (GLE == ERROR_CALL_NOT_IMPLEMENTED)
471     {
472         win_skip("Resource calls are not implemented\n");
473         return;
474     }
475 
476     update_empty_exe();
477 
478     for(i=0; i < sizeof( sec_variants ) / sizeof( sec_variants[0] ); i++)
479     {
480         const struct _sec_variants *sec = &sec_variants[i];
481         build_exe( &sec->build );
482         update_resources_none();
483         check_exe( &sec->chk_none );
484         update_resources_delete();
485         check_exe( &sec->chk_delete );
486         update_resources_version();
487         check_exe( &sec->chk_version );
488         update_resources_bigdata();
489         check_exe( &sec->chk_bigdata );
490         DeleteFileA( filename );
491     }
492     test_find_resource();
493 }
494