Convert to unix line endings.
[wincetools.git] / loader / himemce-pre.c
1  /* himemce-pre.c - High Memory for Windows CE (preloader)
2    Copyright (C) 2010 g10 Code GmbH
3    Written by Marcus Brinkmann <marcus@g10code.com>
4
5    This file is part of HiMemCE.
6  
7    HiMemCE is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11    
12    HiMemCE is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16    
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #include <windows.h>
23 #include <assert.h>
24
25 #include "debug.h"
26
27 #include "kernel32_kernel_private.h"
28 #include "wine.h"
29 #include "himemce-map-provider.h"
30
31
32 # define page_mask  0xfff
33 # define page_shift 12
34 # define page_size  0x1000
35
36 #define ROUND_SIZE(size) \
37   (((SIZE_T)(size) + page_mask) & ~page_mask)
38
39 #define SECTION_IS_LOW(sec) \
40       (((sec)->Characteristics & IMAGE_SCN_MEM_WRITE) &&        \
41        ! ((sec)->Characteristics & IMAGE_SCN_MEM_SHARED))
42
43
44 /* Find all modules to preload and add them to the map.  */
45 static int
46 find_modules (struct himemce_map *map)
47 {
48   /* Five for "*.dll" and one for good luck.  */
49   wchar_t dirname[MAX_PATH + 6];
50   wchar_t filename[2 * MAX_PATH + 1];
51   int res;
52   wchar_t *end;
53   int idx;
54   HANDLE hSearch;
55   WIN32_FIND_DATA FileData;
56   BOOL bFinished = FALSE;
57
58   res = GetModuleFileName (GetModuleHandle (NULL), dirname, MAX_PATH);
59   if (! res)
60     {
61       ERR ("can not determine module filename: %i\n",
62              GetLastError ());
63       return 0;
64     }
65
66   idx = wcslen (dirname);
67   while (idx > 0 && dirname[idx - 1] != '\\' && dirname[idx - 1] != '/')
68     idx--;
69   dirname[idx] = '\0';
70
71   wcscpy (filename, dirname);
72   wcscpy (&dirname[idx], L"*.dll");
73   end = &filename[idx];
74
75   hSearch = FindFirstFile (dirname, &FileData);
76   if (hSearch == INVALID_HANDLE_VALUE)
77     {
78       ERR ("no .dll files found\n");
79       return 0;
80     }
81
82   while (!bFinished)
83     {
84       struct himemce_module *mod;
85       struct binary_info info;
86       HANDLE hnd;
87
88       TRACE ("considering %S: ", FileData.cFileName);
89
90       wcscpy (end, FileData.cFileName);
91
92       if (FileData.cFileName[0] != L'Q')
93         {
94           TRACE ("skip non-Qt library for testing\n");
95           goto skipit;
96         }
97
98       hnd = CreateFile (filename, GENERIC_READ, FILE_SHARE_READ, NULL,
99                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
100       if (hnd == INVALID_HANDLE_VALUE)
101         {
102           TRACE ("skip (probe failure: %i)\n", GetLastError ());
103           goto skipit;
104         }
105
106       MODULE_get_binary_info (hnd, &info);
107       CloseHandle (hnd);
108       if (info.machine != IMAGE_FILE_MACHINE_THUMB)
109         {
110           TRACE ("skip (machine type: %04x)\n", info.machine);
111           goto skipit;
112         }
113
114       /* FIXME: Keep a blacklist.  Maybe exclude ARM (not THUMB)
115          binaries automatically (gpgme and friends).  */
116
117       TRACE ("accept [%2i]\n", map->nr_modules);
118       mod = map_add_module (map, filename, 0);
119       if (! mod)
120         return 0;
121       
122     skipit:
123       if (!FindNextFile (hSearch, &FileData))
124         {
125           bFinished = TRUE;
126           
127           if (GetLastError () != ERROR_NO_MORE_FILES)
128             {
129               ERR ("unable to find next .dll file\n");
130               return 0;
131             }
132         }
133     }
134   if (!FindClose (hSearch))
135     {
136       ERR ("unable to close search handle: %i\n", GetLastError ());
137       return 0;
138     }
139   return 1;
140 }
141
142
143 static SIZE_T
144 section_size (IMAGE_SECTION_HEADER *sec)
145 {
146   static const SIZE_T sector_align = 0x1ff;
147   SIZE_T map_size, file_size, end;
148   
149   if (!sec->Misc.VirtualSize)
150     map_size = ROUND_SIZE( sec->SizeOfRawData );
151   else
152     map_size = ROUND_SIZE( sec->Misc.VirtualSize );
153   
154   file_size = (sec->SizeOfRawData + (sec->PointerToRawData & sector_align) + sector_align) & ~sector_align;
155   if (file_size > map_size) file_size = map_size;
156   end = ROUND_SIZE( file_size );
157   if (end > map_size) end = map_size;
158   return end;
159 }
160
161
162 static void *
163 get_rva_low (char *module, size_t rva)
164 {
165   IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)module;
166   IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(module + dos->e_lfanew);
167   IMAGE_SECTION_HEADER *sec;
168   int sec_cnt;
169   int idx;
170
171   sec = (IMAGE_SECTION_HEADER*)((char*)&nt->OptionalHeader+nt->FileHeader.SizeOfOptionalHeader);
172   sec_cnt = nt->FileHeader.NumberOfSections;
173
174   for (idx = 0; idx < sec_cnt; idx++)
175     {
176       if (! sec[idx].PointerToLinenumbers)
177         continue;
178       if (rva >= sec[idx].VirtualAddress
179           && rva < sec[idx].VirtualAddress + section_size (&sec[idx]))
180                 break;
181     }
182   if (idx == sec_cnt)
183     return (void *)((char *)module + rva);
184   
185   return (void *)((char *)sec[idx].PointerToLinenumbers
186                   + (rva - sec[idx].VirtualAddress));
187 }
188
189   
190 static IMAGE_BASE_RELOCATION *
191 LowLdrProcessRelocationBlock (void *base, void *page, UINT count,
192                               USHORT *relocs)
193 {
194   char *ptr;
195   IMAGE_DOS_HEADER *dos;
196   IMAGE_NT_HEADERS *nt;
197   IMAGE_SECTION_HEADER *sec;
198   int sec_cnt;
199   int idx;
200
201   ptr = base;
202   dos = (IMAGE_DOS_HEADER *) ptr;
203   nt = (IMAGE_NT_HEADERS *) (ptr + dos->e_lfanew);
204   sec = (IMAGE_SECTION_HEADER *) ((char*) &nt->OptionalHeader
205                                   + nt->FileHeader.SizeOfOptionalHeader);
206   sec_cnt = nt->FileHeader.NumberOfSections;
207   idx = sec_cnt;
208
209   /* Small optimization: Exclude read-only sections at the start and
210      end of the list.  */
211   while (sec_cnt > 0 && SECTION_IS_LOW (sec))
212     {
213       sec++;
214       sec_cnt--;
215     }
216   while (sec_cnt > 0 && SECTION_IS_LOW (&sec[sec_cnt - 1]))
217     sec_cnt--;
218   
219   while (count--)
220     {
221       USHORT offset = *relocs & 0xfff;
222       int type = *relocs >> 12;
223       size_t addr;
224       size_t old_addr;
225       size_t off;
226
227       switch(type)
228         {
229         case IMAGE_REL_BASED_ABSOLUTE:
230           goto nextreloc;
231         case IMAGE_REL_BASED_HIGH:
232           addr = HIWORD (*(short *)((char *)page + offset));
233           break;
234         case IMAGE_REL_BASED_LOW:
235           addr = LOWORD (*(short *)((char *)page + offset));
236           break;
237         case IMAGE_REL_BASED_HIGHLOW:
238           addr = *(int *)((char *)page + offset);
239           break;
240         default:
241           TRACE("Unknown/unsupported fixup type %x.\n", type);
242           goto nextreloc;
243         }
244
245       if ((void *) addr < base)
246         {
247           ERR ("ignoring relocation that points below image");
248           goto nextreloc;
249         }
250       off = ((char *) addr) - ((char *) base);
251
252       /* Check if ADDR points into a rw segment.  First check the
253          cached index.  */
254       if (idx < sec_cnt)
255         {
256           if (off >= sec[idx].VirtualAddress
257               && off < sec[idx].VirtualAddress + section_size (&sec[idx]))
258             ; /* Found it.  */
259           else
260             idx = sec_cnt;
261         }
262       if (idx == sec_cnt)
263         {
264           for (idx = 0; idx < sec_cnt; idx++)
265             {
266               if (! sec[idx].PointerToLinenumbers)
267                 continue;
268               if (off >= sec[idx].VirtualAddress
269                   && off < sec[idx].VirtualAddress + section_size (&sec[idx]))
270                 break;
271             }
272           if (idx == sec_cnt)
273             goto nextreloc;
274         }
275       old_addr = addr;
276       addr = sec[idx].PointerToLinenumbers + (off - sec[idx].VirtualAddress);
277
278 #if 0
279       TRACE ("rewriting relocation at %p to rw section from %p to %p\n",
280              ((char *)page + offset), old_addr, addr);
281 #endif
282
283       switch(type)
284         {
285         case IMAGE_REL_BASED_HIGH:
286           *(short *)((char *)page + offset) = HIWORD(addr);
287           break;
288         case IMAGE_REL_BASED_LOW:
289           *(short *)((char *)page + offset) = LOWORD(addr);
290           break;
291         case IMAGE_REL_BASED_HIGHLOW:
292           *(int *)((char *)page + offset) = addr;
293           break;
294         }
295     nextreloc:
296       relocs++;
297     }
298   return (IMAGE_BASE_RELOCATION *)relocs;  /* return address of next block */
299 }
300
301
302 static void
303 relocate_rw_sections (struct himemce_map *map, void *base)
304 {
305   char *ptr;
306   IMAGE_DOS_HEADER *dos;
307   IMAGE_NT_HEADERS *nt;
308   IMAGE_SECTION_HEADER *sec;
309   int i;
310   IMAGE_BASE_RELOCATION *rel, *end;
311   const IMAGE_DATA_DIRECTORY *relocs;
312
313   TRACE ("adjusting rw sections at %p\n", base);
314
315   ptr = base;
316   dos = (IMAGE_DOS_HEADER *) ptr;
317   nt = (IMAGE_NT_HEADERS *) (ptr + dos->e_lfanew);
318   sec = (IMAGE_SECTION_HEADER *) ((char*) &nt->OptionalHeader
319                                   + nt->FileHeader.SizeOfOptionalHeader);
320
321   /* Go through all the sections, reserve low memory for the writable
322      sections.  */
323   for (i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
324     {
325       if (SECTION_IS_LOW (sec))
326         {
327           SIZE_T map_size;
328
329           if (! sec->Misc.VirtualSize)
330             map_size = ROUND_SIZE (sec->SizeOfRawData);
331           else
332             map_size = ROUND_SIZE (sec->Misc.VirtualSize);
333
334           sec->PointerToLinenumbers = (DWORD) map_reserve_low (map, map_size);
335
336           TRACE ("mapping r/w section %.8s at %p off %x (%lx) flags "
337                  "%x to low mem %p\n",
338                  sec->Name, ptr + sec->VirtualAddress,
339                  sec->PointerToRawData, map_size,
340                  sec->Characteristics, sec->PointerToLinenumbers);
341         }
342       else
343         sec->PointerToLinenumbers = 0;
344     }
345
346   /* Perform base relocations pointing into low sections.  Before
347      that, these relocations point into the high mem address.  */
348
349   relocs = &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
350   rel = (IMAGE_BASE_RELOCATION *)(ptr + relocs->VirtualAddress);
351   end = (IMAGE_BASE_RELOCATION *)(ptr + relocs->VirtualAddress + relocs->Size);
352   
353   while (rel < end - 1 && rel->SizeOfBlock)
354     {
355       rel = LowLdrProcessRelocationBlock
356         (base, ptr + rel->VirtualAddress,
357          (rel->SizeOfBlock - sizeof (*rel)) / sizeof (USHORT),
358          (USHORT *)(rel + 1));
359     }
360 }
361
362
363 /* convert PE image VirtualAddress to Real Address */
364 static void *
365 get_rva (HMODULE module, DWORD va)
366 {
367   return (void *) ((char *) module + va);
368 }
369
370
371 /* convert from straight ASCII to Unicode without depending on the
372    current codepage */
373 static void
374 ascii_to_unicode (WCHAR *dst, const char *src, size_t len)
375 {
376   while (len--)
377     *dst++ = (unsigned char) *src++;
378 }
379
380
381 #define allocate_stub(x,y) ((void *)0xdeadbeef)
382
383
384 static FARPROC
385 find_ordinal_export (void *module, const IMAGE_EXPORT_DIRECTORY *exports,
386                      DWORD exp_size, DWORD ordinal)
387 {
388   FARPROC proc;
389   const DWORD *functions = get_rva (module, exports->AddressOfFunctions);
390   
391   if (ordinal >= exports->NumberOfFunctions)
392     {
393       TRACE(" ordinal %d out of range!\n", ordinal + exports->Base );
394       return NULL;
395     }
396   if (!functions[ordinal]) return NULL;
397   
398 #if 0
399   /* if the address falls into the export dir, it's a forward */
400   if (((const char *)proc >= (const char *)exports) && 
401       ((const char *)proc < (const char *)exports + exp_size))
402     return find_forwarded_export( module, (const char *)proc, load_path );
403 #endif
404
405   proc = get_rva_low (module, functions[ordinal]);
406   return proc;
407 }
408
409
410 static FARPROC
411 find_named_export (void *module, const IMAGE_EXPORT_DIRECTORY *exports,
412                    DWORD exp_size, const char *name, int hint)
413 {
414   const WORD *ordinals = get_rva (module, exports->AddressOfNameOrdinals);
415   const DWORD *names = get_rva (module, exports->AddressOfNames);
416   int min = 0, max = exports->NumberOfNames - 1;
417
418   /* first check the hint */
419   if (hint >= 0 && hint <= max)
420     {
421       char *ename = get_rva( module, names[hint] );
422       if (!strcmp( ename, name ))
423         return find_ordinal_export( module, exports, exp_size, ordinals[hint]);
424     }
425
426   /* then do a binary search */
427   while (min <= max)
428     {
429       int res, pos = (min + max) / 2;
430       char *ename = get_rva( module, names[pos] );
431       if (!(res = strcmp( ename, name )))
432         return find_ordinal_export( module, exports, exp_size, ordinals[pos]);
433       if (res > 0) max = pos - 1;
434       else min = pos + 1;
435     }
436   return NULL;
437 }
438
439
440 /*************************************************************************
441  *              import_dll
442  *
443  * Import the dll specified by the given import descriptor.
444  * The loader_section must be locked while calling this function.
445  */
446 static void *
447 import_dll (struct himemce_map *map, HMODULE module,
448             const IMAGE_IMPORT_DESCRIPTOR *descr)
449 {
450   int status = 0;
451   const char *name = get_rva (module, descr->Name);
452   DWORD len = strlen(name);
453   const IMAGE_THUNK_DATA *import_list;
454   IMAGE_THUNK_DATA *thunk_list;
455   void *imp_base = 0;
456   HMODULE imp_mod = 0;
457   const IMAGE_EXPORT_DIRECTORY *exports;
458   DWORD exp_size;
459   WCHAR buffer[32];
460   int i;
461
462   thunk_list = get_rva (module, (DWORD)descr->FirstThunk);
463   if (descr->OriginalFirstThunk)
464     import_list = get_rva (module, (DWORD)descr->OriginalFirstThunk);
465   else
466     import_list = thunk_list;
467
468   while (len && name[len-1] == ' ') len--;  /* remove trailing spaces */
469
470   /* First check for the modules in the map.  */
471   for (i = 0; i < map->nr_modules; i++)
472     {
473       if (! strncmp (name, map->module[i].name, len))
474         break;
475     }
476   if (i < map->nr_modules)
477     {
478       imp_base = map->module[i].base;
479       TRACE("Loading library %s internal\n", name);
480     }
481   else if (len * sizeof(WCHAR) < sizeof(buffer))
482     {
483       ascii_to_unicode( buffer, name, len );
484       buffer[len] = 0;
485       imp_mod = LoadLibrary (buffer);
486       if (imp_mod == INVALID_HANDLE_VALUE)
487         status = GetLastError ();
488     }
489   else
490     {
491       WCHAR *ptr = malloc ((len + 1) * sizeof(WCHAR) );
492       if (!ptr) return NULL;
493       ascii_to_unicode( ptr, name, len );
494       ptr[len] = 0;
495       imp_mod = LoadLibrary (ptr);
496       if (imp_mod == INVALID_HANDLE_VALUE)
497         status = GetLastError ();
498       free (ptr);
499     }
500   if (status)
501     {
502       if (status == ERROR_DLL_NOT_FOUND)
503         TRACE("Library %s not found\n", name);
504       else
505         TRACE("Loading library %s failed (error %x).\n",  name, status);
506       return NULL;
507     }
508
509   if (imp_base)
510     {
511       exports = MyRtlImageDirectoryEntryToData (imp_base, TRUE,
512                                                 IMAGE_DIRECTORY_ENTRY_EXPORT,
513                                                 &exp_size);
514       if (!exports)
515         {
516           /* set all imported function to deadbeef */
517           while (import_list->u1.Ordinal)
518             {
519               if (IMAGE_SNAP_BY_ORDINAL(import_list->u1.Ordinal))
520                 {
521                   int ordinal = IMAGE_ORDINAL(import_list->u1.Ordinal);
522                   TRACE ("No implementation for %s.%d", name, ordinal);
523                   thunk_list->u1.Function
524                     = (PDWORD) allocate_stub (name, IntToPtr (ordinal));
525                 }
526               else
527                 {
528                   IMAGE_IMPORT_BY_NAME *pe_name
529                     = get_rva (module, (DWORD) import_list->u1.AddressOfData);
530                   TRACE ("No implementation for %s.%s", name, pe_name->Name);
531                   thunk_list->u1.Function
532                     = (PDWORD) allocate_stub (name, (const char*) pe_name->Name);
533                 }
534               import_list++;
535               thunk_list++;
536             }
537           goto done;
538         }
539     }
540   
541   while (import_list->u1.Ordinal)
542     {
543       if (IMAGE_SNAP_BY_ORDINAL(import_list->u1.Ordinal))
544         {
545           int ordinal = IMAGE_ORDINAL(import_list->u1.Ordinal);
546
547           if (imp_base)
548             thunk_list->u1.Function = (PDWORD)(ULONG_PTR)
549               find_ordinal_export (imp_base, exports, exp_size,
550                                    ordinal - exports->Base);
551           else
552             thunk_list->u1.Function = (PDWORD)(ULONG_PTR)
553               GetProcAddress (imp_mod, (void *) (ordinal & 0xffff));
554           if (!thunk_list->u1.Function)
555             {
556               thunk_list->u1.Function = (PDWORD) allocate_stub( name, IntToPtr(ordinal) );
557               TRACE("No implementation for %s.%d imported, setting to %p\n",
558                     name, ordinal,
559                     (void *)thunk_list->u1.Function );
560             }
561           TRACE("--- Ordinal %s.%d = %p\n", name, ordinal, (void *)thunk_list->u1.Function );
562         }
563       else  /* import by name */
564         {
565           IMAGE_IMPORT_BY_NAME *pe_name;
566           pe_name = get_rva( module, (DWORD)import_list->u1.AddressOfData );
567           if (imp_base)
568             thunk_list->u1.Function = (PDWORD)(ULONG_PTR)
569               find_named_export (imp_base, exports, exp_size,
570                                  (const char*)pe_name->Name, pe_name->Hint);
571           else
572             thunk_list->u1.Function = (PDWORD)(ULONG_PTR)
573               GetProcAddressA (imp_mod, (const char*)pe_name->Name);
574           if (!thunk_list->u1.Function)
575             {
576               thunk_list->u1.Function
577                 = (PDWORD) allocate_stub (name, (const char*)pe_name->Name);
578               TRACE ("No implementation for %s.%s imported, setting to %p\n",
579                      name, pe_name->Name, (void *)thunk_list->u1.Function);
580             }
581           TRACE("--- %s %s.%d = %p\n",
582                 pe_name->Name, name, pe_name->Hint,
583                 (void *)thunk_list->u1.Function);
584         }
585       import_list++;
586       thunk_list++;
587     }
588
589  done:
590   return (void*)1;
591 }
592
593
594 static void
595 fixup_imports (struct himemce_map *map, void *base)
596 {
597   int i, nb_imports;
598   const IMAGE_IMPORT_DESCRIPTOR *imports;
599   DWORD size;
600
601   imports = MyRtlImageDirectoryEntryToData (base, TRUE,
602                                             IMAGE_DIRECTORY_ENTRY_IMPORT,
603                                             &size);
604   if (!imports)
605     return;
606
607   nb_imports = 0;
608   while (imports[nb_imports].Name && imports[nb_imports].FirstThunk)
609     nb_imports++;
610   if (!nb_imports)
611     return;
612
613   for (i = 0; i < nb_imports; i++)
614     {
615       if (! import_dll (map, base, &imports[i]))
616         {
617           SetLastError (ERROR_DLL_NOT_FOUND);
618           break;
619         }
620     }
621 }
622
623
624 int
625 main (int argc, char *argv[])
626 {
627   struct himemce_map *map;
628   int result = 0;
629   int i;
630
631   TRACE ("creating map file...\n");
632
633   map = map_create ();
634   if (! map)
635     return 1;
636
637   TRACE ("finding modules...\n");
638
639   result = find_modules (map);
640   if (! result)
641     exit (1);
642
643   TRACE ("loading modules...\n");
644
645   /* For each module: load it high without resolving references.  */
646   for (i = 0; i < map->nr_modules; i++)
647     {
648       struct himemce_module *mod = &map->module[i];
649       void *base = MyLoadLibraryExW (mod->filename, 0,
650                                      DONT_RESOLVE_DLL_REFERENCES);
651
652       if (! base)
653         {
654           ERR ("could not load %S: %i\n", mod->filename, GetLastError());
655           exit (1);
656         }
657       mod->base = base;
658     }
659
660   TRACE ("relocationg writable sections...\n");
661
662   for (i = 0; i < map->nr_modules; i++)
663     {
664       struct himemce_module *mod = &map->module[i];
665
666       /* Allocate low mem for read-write sections and adjust
667          relocations pointing into them.  */
668       relocate_rw_sections (map, mod->base);
669     }
670
671   /* Export entries are handled at time of import on the other side,
672      when we check for low memory mapped sections and adjust the
673      imported address accordingly.  */
674
675   TRACE ("resolve module dependencies...\n");
676
677   for (i = 0; i < map->nr_modules; i++)
678     {
679       struct himemce_module *mod = &map->module[i];
680
681       /* Fixup imports (this loads all dependencies as well!).  */
682       fixup_imports (map, mod->base);
683     }
684
685   TRACE ("sleeping...");
686
687   while (1)
688     Sleep (3600 * 1000);
689
690   return 0;
691 }