Added partial property set support which is used by dxdiag.exe.
[wine] / libs / wine / loader.c
1 /*
2  * Win32 builtin dlls support
3  *
4  * Copyright 2000 Alexandre Julliard
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #ifdef HAVE_SYS_MMAN_H
31 #include <sys/mman.h>
32 #endif
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 #ifdef HAVE_STDINT_H
37 # include <stdint.h>
38 #endif
39
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
42 #include "windef.h"
43 #include "wine/library.h"
44
45 /* argc/argv for the Windows application */
46 int __wine_main_argc = 0;
47 char **__wine_main_argv = NULL;
48 WCHAR **__wine_main_wargv = NULL;
49
50 #define MAX_DLLS 100
51
52 static struct
53 {
54     const IMAGE_NT_HEADERS *nt;           /* NT header */
55     const char             *filename;     /* DLL file name */
56 } builtin_dlls[MAX_DLLS];
57
58 static int nb_dlls;
59
60 static const IMAGE_NT_HEADERS *main_exe;
61
62 static load_dll_callback_t load_dll_callback;
63
64 static const char **dll_paths;
65 static int nb_dll_paths;
66 static int dll_path_maxlen;
67 static int init_done;
68
69
70 /* build the dll load path from the WINEDLLPATH variable */
71 static void build_dll_path(void)
72 {
73     static const char * const dlldir = DLLDIR;
74     int len, count = 0;
75     char *p, *path = getenv( "WINEDLLPATH" );
76
77     init_done = 1;
78
79     if (path)
80     {
81         /* count how many path elements we need */
82         path = strdup(path);
83         p = path;
84         while (*p)
85         {
86             while (*p == ':') p++;
87             if (!*p) break;
88             count++;
89             while (*p && *p != ':') p++;
90         }
91     }
92
93     dll_paths = malloc( (count+1) * sizeof(*dll_paths) );
94
95     if (count)
96     {
97         p = path;
98         nb_dll_paths = 0;
99         while (*p)
100         {
101             while (*p == ':') *p++ = 0;
102             if (!*p) break;
103             dll_paths[nb_dll_paths] = p;
104             while (*p && *p != ':') p++;
105             if (p - dll_paths[nb_dll_paths] > dll_path_maxlen)
106                 dll_path_maxlen = p - dll_paths[nb_dll_paths];
107             nb_dll_paths++;
108         }
109     }
110
111     /* append default dll dir (if not empty) to path */
112     if ((len = strlen(dlldir)))
113     {
114         if (len > dll_path_maxlen) dll_path_maxlen = len;
115         dll_paths[nb_dll_paths++] = dlldir;
116     }
117 }
118
119 /* check if a given file can be opened */
120 inline static int file_exists( const char *name )
121 {
122     int fd = open( name, O_RDONLY );
123     if (fd != -1) close( fd );
124     return (fd != -1);
125 }
126
127 /* open a library for a given dll, searching in the dll path
128  * 'name' must be the Windows dll name (e.g. "kernel32.dll") */
129 static void *dlopen_dll( const char *name, char *error, int errorsize, int test_only )
130 {
131     int i, namelen = strlen(name);
132     char *buffer, *p;
133     void *ret = NULL;
134
135     if (!init_done) build_dll_path();
136
137     buffer = malloc( dll_path_maxlen + namelen + 5 );
138
139     /* store the name at the end of the buffer, followed by .so */
140     p = buffer + dll_path_maxlen;
141     *p++ = '/';
142     memcpy( p, name, namelen );
143     strcpy( p + namelen, ".so" );
144
145     for (i = 0; i < nb_dll_paths; i++)
146     {
147         int len = strlen(dll_paths[i]);
148         p = buffer + dll_path_maxlen - len;
149         memcpy( p, dll_paths[i], len );
150         if (test_only)  /* just test for file existence */
151         {
152             if ((ret = (void *)file_exists( p ))) break;
153         }
154         else
155         {
156             if ((ret = wine_dlopen( p, RTLD_NOW, error, errorsize ))) break;
157             if (file_exists( p )) break; /* exists but cannot be loaded, return the error */
158         }
159     }
160     free( buffer );
161     return ret;
162 }
163
164
165 /* adjust an array of pointers to make them into RVAs */
166 static inline void fixup_rva_ptrs( void *array, void *base, int count )
167 {
168     void **ptr = (void **)array;
169     while (count--)
170     {
171         if (*ptr) *ptr = (void *)((char *)*ptr - (char *)base);
172         ptr++;
173     }
174 }
175
176
177 /* fixup RVAs in the import directory */
178 static void fixup_imports( IMAGE_IMPORT_DESCRIPTOR *dir, DWORD size, void *base )
179 {
180     int count = size / sizeof(void *);
181     void **ptr = (void **)dir;
182
183     /* everything is either a pointer or a ordinal value below 0x10000 */
184     while (count--)
185     {
186         if (*ptr >= (void *)0x10000) *ptr = (void *)((char *)*ptr - (char *)base);
187         else if (*ptr) *ptr = (void *)(0x80000000 | (unsigned int)*ptr);
188         ptr++;
189     }
190 }
191
192
193 /* fixup RVAs in the resource directory */
194 static void fixup_resources( IMAGE_RESOURCE_DIRECTORY *dir, char *root, void *base )
195 {
196     IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
197     int i;
198
199     entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
200     for (i = 0; i < dir->NumberOfNamedEntries + dir->NumberOfIdEntries; i++, entry++)
201     {
202         void *ptr = root + entry->u2.s3.OffsetToDirectory;
203         if (entry->u2.s3.DataIsDirectory) fixup_resources( ptr, root, base );
204         else
205         {
206             IMAGE_RESOURCE_DATA_ENTRY *data = ptr;
207             fixup_rva_ptrs( &data->OffsetToData, base, 1 );
208         }
209     }
210 }
211
212
213 /* map a builtin dll in memory and fixup RVAs */
214 static void *map_dll( const IMAGE_NT_HEADERS *nt_descr )
215 {
216 #ifdef HAVE_MMAP
217     IMAGE_DATA_DIRECTORY *dir;
218     IMAGE_DOS_HEADER *dos;
219     IMAGE_NT_HEADERS *nt;
220     IMAGE_SECTION_HEADER *sec;
221     BYTE *addr, *code_start, *data_start;
222     size_t page_size = getpagesize();
223     int nb_sections = 2;  /* code + data */
224
225     size_t size = (sizeof(IMAGE_DOS_HEADER)
226                    + sizeof(IMAGE_NT_HEADERS)
227                    + nb_sections * sizeof(IMAGE_SECTION_HEADER));
228
229     assert( size <= page_size );
230
231     /* module address must be aligned on 64K boundary */
232     addr = (BYTE *)((nt_descr->OptionalHeader.ImageBase + 0xffff) & ~0xffff);
233     if (wine_anon_mmap( addr, page_size, PROT_READ|PROT_WRITE, MAP_FIXED ) != addr) return NULL;
234
235     dos    = (IMAGE_DOS_HEADER *)addr;
236     nt     = (IMAGE_NT_HEADERS *)(dos + 1);
237     sec    = (IMAGE_SECTION_HEADER *)(nt + 1);
238     code_start = addr + page_size;
239
240     /* HACK! */
241     data_start = code_start + page_size;
242
243     /* Build the DOS and NT headers */
244
245     dos->e_magic  = IMAGE_DOS_SIGNATURE;
246     dos->e_lfanew = sizeof(*dos);
247
248     *nt = *nt_descr;
249
250     nt->FileHeader.NumberOfSections                = nb_sections;
251     nt->OptionalHeader.SizeOfCode                  = data_start - code_start;
252     nt->OptionalHeader.SizeOfInitializedData       = 0;
253     nt->OptionalHeader.SizeOfUninitializedData     = 0;
254     nt->OptionalHeader.ImageBase                   = (DWORD)addr;
255
256     fixup_rva_ptrs( &nt->OptionalHeader.AddressOfEntryPoint, addr, 1 );
257
258     /* Build the code section */
259
260     strcpy( sec->Name, ".text" );
261     sec->SizeOfRawData = data_start - code_start;
262     sec->Misc.VirtualSize = sec->SizeOfRawData;
263     sec->VirtualAddress   = code_start - addr;
264     sec->PointerToRawData = code_start - addr;
265     sec->Characteristics  = (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ);
266     sec++;
267
268     /* Build the data section */
269
270     strcpy( sec->Name, ".data" );
271     sec->SizeOfRawData = 0;
272     sec->Misc.VirtualSize = sec->SizeOfRawData;
273     sec->VirtualAddress   = data_start - addr;
274     sec->PointerToRawData = data_start - addr;
275     sec->Characteristics  = (IMAGE_SCN_CNT_INITIALIZED_DATA |
276                              IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ);
277     sec++;
278
279     /* Build the import directory */
280
281     dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_IMPORT_DIRECTORY];
282     if (dir->Size)
283     {
284         IMAGE_IMPORT_DESCRIPTOR *imports = (void *)dir->VirtualAddress;
285         fixup_rva_ptrs( &dir->VirtualAddress, addr, 1 );
286         fixup_imports( imports, dir->Size, addr );
287     }
288
289     /* Build the resource directory */
290
291     dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_RESOURCE_DIRECTORY];
292     if (dir->Size)
293     {
294         void *ptr = (void *)dir->VirtualAddress;
295         fixup_rva_ptrs( &dir->VirtualAddress, addr, 1 );
296         fixup_resources( ptr, ptr, addr );
297     }
298
299     /* Build the export directory */
300
301     dir = &nt->OptionalHeader.DataDirectory[IMAGE_FILE_EXPORT_DIRECTORY];
302     if (dir->Size)
303     {
304         IMAGE_EXPORT_DIRECTORY *exports = (void *)dir->VirtualAddress;
305         fixup_rva_ptrs( &dir->VirtualAddress, addr, 1 );
306         fixup_rva_ptrs( (void *)exports->AddressOfFunctions, addr, exports->NumberOfFunctions );
307         fixup_rva_ptrs( (void *)exports->AddressOfNames, addr, exports->NumberOfNames );
308         fixup_rva_ptrs( &exports->Name, addr, 1 );
309         fixup_rva_ptrs( &exports->AddressOfFunctions, addr, 1 );
310         fixup_rva_ptrs( &exports->AddressOfNames, addr, 1 );
311         fixup_rva_ptrs( &exports->AddressOfNameOrdinals, addr, 1 );
312     }
313     return addr;
314 #else  /* HAVE_MMAP */
315     return NULL;
316 #endif  /* HAVE_MMAP */
317 }
318
319
320 /***********************************************************************
321  *           __wine_dll_register
322  *
323  * Register a built-in DLL descriptor.
324  */
325 void __wine_dll_register( const IMAGE_NT_HEADERS *header, const char *filename )
326 {
327     if (load_dll_callback) load_dll_callback( map_dll(header), filename );
328     else
329     {
330         if (!(header->FileHeader.Characteristics & IMAGE_FILE_DLL))
331             main_exe = header;
332         else
333         {
334             assert( nb_dlls < MAX_DLLS );
335             builtin_dlls[nb_dlls].nt = header;
336             builtin_dlls[nb_dlls].filename = filename;
337             nb_dlls++;
338         }
339     }
340 }
341
342
343 /***********************************************************************
344  *           wine_dll_set_callback
345  *
346  * Set the callback function for dll loading, and call it
347  * for all dlls that were implicitly loaded already.
348  */
349 void wine_dll_set_callback( load_dll_callback_t load )
350 {
351     int i;
352     load_dll_callback = load;
353     for (i = 0; i < nb_dlls; i++)
354     {
355         const IMAGE_NT_HEADERS *nt = builtin_dlls[i].nt;
356         if (!nt) continue;
357         builtin_dlls[i].nt = NULL;
358         load_dll_callback( map_dll(nt), builtin_dlls[i].filename );
359     }
360     nb_dlls = 0;
361     if (main_exe) load_dll_callback( map_dll(main_exe), "" );
362 }
363
364
365 /***********************************************************************
366  *           wine_dll_load
367  *
368  * Load a builtin dll.
369  */
370 void *wine_dll_load( const char *filename, char *error, int errorsize )
371 {
372     int i;
373
374     /* callback must have been set already */
375     assert( load_dll_callback );
376
377     /* check if we have it in the list */
378     /* this can happen when initializing pre-loaded dlls in wine_dll_set_callback */
379     for (i = 0; i < nb_dlls; i++)
380     {
381         if (!builtin_dlls[i].nt) continue;
382         if (!strcmp( builtin_dlls[i].filename, filename ))
383         {
384             const IMAGE_NT_HEADERS *nt = builtin_dlls[i].nt;
385             builtin_dlls[i].nt = NULL;
386             load_dll_callback( map_dll(nt), builtin_dlls[i].filename );
387             return (void *)1;
388         }
389     }
390     return dlopen_dll( filename, error, errorsize, 0 );
391 }
392
393
394 /***********************************************************************
395  *           wine_dll_unload
396  *
397  * Unload a builtin dll.
398  */
399 void wine_dll_unload( void *handle )
400 {
401     if (handle != (void *)1)
402         wine_dlclose( handle, NULL, 0 );
403 }
404
405
406 /***********************************************************************
407  *           wine_dll_load_main_exe
408  *
409  * Try to load the .so for the main exe.
410  */
411 void *wine_dll_load_main_exe( const char *name, char *error, int errorsize, int test_only )
412 {
413     return dlopen_dll( name, error, errorsize, test_only );
414 }
415
416
417 /***********************************************************************
418  *           wine_init
419  *
420  * Main Wine initialisation.
421  */
422 void wine_init( int argc, char *argv[], char *error, int error_size )
423 {
424     void *ntdll;
425     void (*init_func)(int, char **);
426
427     if (!(ntdll = dlopen_dll( "ntdll.dll", error, error_size, 0 ))) return;
428     if (!(init_func = wine_dlsym( ntdll, "__wine_process_init", error, error_size ))) return;
429     init_func( argc, argv );
430 }
431
432
433 #if defined(__svr4__) || defined(__NetBSD__)
434 /***********************************************************************
435  *             try_mmap_fixed
436  *
437  * The purpose of this routine is to emulate the behaviour of
438  * the Linux mmap() routine if a non-NULL address is passed,
439  * but the MAP_FIXED flag is not set.  Linux in this case tries
440  * to place the mapping at the specified address, *unless* the
441  * range is already in use.  Solaris, however, completely ignores
442  * the address argument in this case.
443  *
444  * As Wine code occasionally relies on the Linux behaviour, e.g. to
445  * be able to map non-relocateable PE executables to their proper
446  * start addresses, or to map the DOS memory to 0, this routine
447  * emulates the Linux behaviour by checking whether the desired
448  * address range is still available, and placing the mapping there
449  * using MAP_FIXED if so.
450  */
451 static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
452                            int fildes, off_t off)
453 {
454     char * volatile result = NULL;
455     int pagesize = getpagesize();
456     pid_t pid;
457
458     /* We only try to map to a fixed address if
459        addr is non-NULL and properly aligned,
460        and MAP_FIXED isn't already specified. */
461
462     if ( !addr )
463         return 0;
464     if ( (uintptr_t)addr & (pagesize-1) )
465         return 0;
466     if ( flags & MAP_FIXED )
467         return 0;
468
469     /* We use vfork() to freeze all threads of the
470        current process.  This allows us to check without
471        race condition whether the desired memory range is
472        already in use.  Note that because vfork() shares
473        the address spaces between parent and child, we
474        can actually perform the mapping in the child. */
475
476     if ( (pid = vfork()) == -1 )
477     {
478         perror("try_mmap_fixed: vfork");
479         exit(1);
480     }
481     if ( pid == 0 )
482     {
483         int i;
484         char vec;
485
486         /* We call mincore() for every page in the desired range.
487            If any of these calls succeeds, the page is already
488            mapped and we must fail. */
489         for ( i = 0; i < len; i += pagesize )
490             if ( mincore( (caddr_t)addr + i, pagesize, &vec ) != -1 )
491                _exit(1);
492
493         /* Perform the mapping with MAP_FIXED set.  This is safe
494            now, as none of the pages is currently in use. */
495         result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off );
496         if ( result == addr )
497             _exit(0);
498
499         if ( result != (void *) -1 ) /* This should never happen ... */
500             munmap( result, len );
501
502        _exit(1);
503     }
504
505     /* vfork() lets the parent continue only after the child
506        has exited.  Furthermore, Wine sets SIGCHLD to SIG_IGN,
507        so we don't need to wait for the child. */
508
509     return result == addr;
510 }
511 #endif  /* __svr4__ || __NetBSD__ */
512
513
514 /***********************************************************************
515  *              wine_anon_mmap
516  *
517  * Portable wrapper for anonymous mmaps
518  */
519 void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
520 {
521 #ifdef HAVE_MMAP
522     static int fdzero = -1;
523
524 #ifdef MAP_ANON
525     flags |= MAP_ANON;
526 #else
527     if (fdzero == -1)
528     {
529         if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
530         {
531             perror( "/dev/zero: open" );
532             exit(1);
533         }
534     }
535 #endif  /* MAP_ANON */
536
537 #ifdef MAP_SHARED
538     flags &= ~MAP_SHARED;
539 #endif
540
541     /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
542 #ifdef MAP_PRIVATE
543     flags |= MAP_PRIVATE;
544 #endif
545
546 #if defined(__svr4__) || defined(__NetBSD__)
547     if ( try_mmap_fixed( start, size, prot, flags, fdzero, 0 ) )
548         return start;
549 #endif
550
551     return mmap( start, size, prot, flags, fdzero, 0 );
552 #else
553     return (void *)-1;
554 #endif
555 }
556
557
558 /*
559  * These functions provide wrappers around dlopen() and associated
560  * functions.  They work around a bug in glibc 2.1.x where calling
561  * a dl*() function after a previous dl*() function has failed
562  * without a dlerror() call between the two will cause a crash.
563  * They all take a pointer to a buffer that
564  * will receive the error description (from dlerror()).  This
565  * parameter may be NULL if the error description is not required.
566  */
567
568 /***********************************************************************
569  *              wine_dlopen
570  */
571 void *wine_dlopen( const char *filename, int flag, char *error, int errorsize )
572 {
573 #ifdef HAVE_DLOPEN
574     void *ret;
575     const char *s;
576     dlerror(); dlerror();
577     ret = dlopen( filename, flag );
578     s = dlerror();
579     if (error)
580     {
581         strncpy( error, s ? s : "", errorsize );
582         error[errorsize - 1] = '\0';
583     }
584     dlerror();
585     return ret;
586 #else
587     if (error)
588     {
589         strncpy( error, "dlopen interface not detected by configure", errorsize );
590         error[errorsize - 1] = '\0';
591     }
592     return NULL;
593 #endif
594 }
595
596 /***********************************************************************
597  *              wine_dlsym
598  */
599 void *wine_dlsym( void *handle, const char *symbol, char *error, int errorsize )
600 {
601 #ifdef HAVE_DLOPEN
602     void *ret;
603     const char *s;
604     dlerror(); dlerror();
605     ret = dlsym( handle, symbol );
606     s = dlerror();
607     if (error)
608     {
609         strncpy( error, s ? s : "", errorsize );
610         error[errorsize - 1] = '\0';
611     }
612     dlerror();
613     return ret;
614 #else
615     if (error)
616     {
617         strncpy( error, "dlopen interface not detected by configure", errorsize );
618         error[errorsize - 1] = '\0';
619     }
620     return NULL;
621 #endif
622 }
623
624 /***********************************************************************
625  *              wine_dlclose
626  */
627 int wine_dlclose( void *handle, char *error, int errorsize )
628 {
629 #ifdef HAVE_DLOPEN
630     int ret;
631     const char *s;
632     dlerror(); dlerror();
633     ret = dlclose( handle );
634     s = dlerror();
635     if (error)
636     {
637         strncpy( error, s ? s : "", errorsize );
638         error[errorsize - 1] = '\0';
639     }
640     dlerror();
641     return ret;
642 #else
643     if (error)
644     {
645         strncpy( error, "dlopen interface not detected by configure", errorsize );
646         error[errorsize - 1] = '\0';
647     }
648     return 1;
649 #endif
650 }