- implementation of LdrUnloadDll out of loader/module.c
[wine] / dlls / ntdll / loader.c
1 /*
2  * Copyright 2002 Dmitry Timoshkov for Codeweavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #include "winbase.h"
20 #include "winnt.h"
21 #include "winternl.h"
22
23 #include "module.h"
24 #include "file.h"
25 #include "wine/exception.h"
26 #include "excpt.h"
27 #include "wine/debug.h"
28 #include "wine/server.h"
29 #include "ntdll_misc.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
32 WINE_DECLARE_DEBUG_CHANNEL(module);
33 WINE_DECLARE_DEBUG_CHANNEL(module);
34 WINE_DECLARE_DEBUG_CHANNEL(loaddll);
35
36 static int free_lib_count;   /* recursion depth of FreeLibrary calls */
37
38 /* filter for page-fault exceptions */
39 static WINE_EXCEPTION_FILTER(page_fault)
40 {
41     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
42         return EXCEPTION_EXECUTE_HANDLER;
43     return EXCEPTION_CONTINUE_SEARCH;
44 }
45
46
47 /******************************************************************
48  *              LdrDisableThreadCalloutsForDll (NTDLL.@)
49  *
50  */
51 NTSTATUS WINAPI LdrDisableThreadCalloutsForDll(HMODULE hModule)
52 {
53     WINE_MODREF *wm;
54     NTSTATUS    ret = STATUS_SUCCESS;
55
56     RtlEnterCriticalSection( &loader_section );
57
58     wm = MODULE32_LookupHMODULE( hModule );
59     if ( !wm )
60         ret = STATUS_DLL_NOT_FOUND;
61     else
62         wm->flags |= WINE_MODREF_NO_DLL_CALLS;
63
64     RtlLeaveCriticalSection( &loader_section );
65
66     return ret;
67 }
68
69 /**********************************************************************
70  *          MODULE_FindModule
71  *
72  * Find a (loaded) win32 module depending on path
73  *      LPCSTR path: [in] pathname of module/library to be found
74  *
75  * The loader_section must be locked while calling this function
76  * RETURNS
77  *      the module handle if found
78  *      0 if not
79  */
80 WINE_MODREF *MODULE_FindModule(LPCSTR path)
81 {
82     WINE_MODREF *wm;
83     char dllname[260], *p;
84
85     /* Append .DLL to name if no extension present */
86     strcpy( dllname, path );
87     if (!(p = strrchr( dllname, '.')) || strchr( p, '/' ) || strchr( p, '\\'))
88             strcat( dllname, ".DLL" );
89
90     for ( wm = MODULE_modref_list; wm; wm = wm->next )
91     {
92         if ( !FILE_strcasecmp( dllname, wm->modname ) )
93             break;
94         if ( !FILE_strcasecmp( dllname, wm->filename ) )
95             break;
96         if ( !FILE_strcasecmp( dllname, wm->short_modname ) )
97             break;
98         if ( !FILE_strcasecmp( dllname, wm->short_filename ) )
99             break;
100     }
101
102     return wm;
103 }
104
105 /******************************************************************
106  *              LdrGetDllHandle (NTDLL.@)
107  *
108  *
109  */
110 NTSTATUS WINAPI LdrGetDllHandle(ULONG x, ULONG y, PUNICODE_STRING name, HMODULE *base)
111 {
112     WINE_MODREF *wm;
113
114     TRACE("%08lx %08lx %s %p\n",
115           x, y, name ? debugstr_wn(name->Buffer, name->Length) : NULL, base);
116
117     if (x != 0 || y != 0)
118         FIXME("Unknown behavior, please report\n");
119
120     /* FIXME: we should store module name information as unicode */
121     if (name)
122     {
123         STRING str;
124
125         RtlUnicodeStringToAnsiString( &str, name, TRUE );
126
127         wm = MODULE_FindModule( str.Buffer );
128         RtlFreeAnsiString( &str );
129     }
130     else
131         wm = exe_modref;
132
133     if (!wm)
134     {
135         *base = 0;
136         return STATUS_DLL_NOT_FOUND;
137     }
138
139     *base = wm->module;
140     return STATUS_SUCCESS;
141 }
142
143 /***********************************************************************
144  *           MODULE_GetProcAddress              (internal)
145  */
146 FARPROC MODULE_GetProcAddress(
147         HMODULE hModule,        /* [in] current module handle */
148         LPCSTR function,        /* [in] function to be looked up */
149         int hint,
150         BOOL snoop )
151 {
152     WINE_MODREF *wm;
153     FARPROC     retproc = 0;
154
155     if (HIWORD(function))
156         TRACE("(%p,%s (%d))\n",hModule,function,hint);
157     else
158         TRACE("(%p,%p)\n",hModule,function);
159
160     RtlEnterCriticalSection( &loader_section );
161     if ((wm = MODULE32_LookupHMODULE( hModule )))
162     {
163         retproc = wm->find_export( wm, function, hint, snoop );
164     }
165     RtlLeaveCriticalSection( &loader_section );
166     return retproc;
167 }
168
169
170 /******************************************************************
171  *              LdrGetProcedureAddress (NTDLL.@)
172  *
173  *
174  */
175 NTSTATUS WINAPI LdrGetProcedureAddress(HMODULE base, PANSI_STRING name, ULONG ord, PVOID *address)
176 {
177     WARN("%p %s %ld %p\n", base, name ? debugstr_an(name->Buffer, name->Length) : NULL, ord, address);
178
179     *address = MODULE_GetProcAddress( base, name ? name->Buffer : (LPSTR)ord, -1, TRUE );
180
181     return (*address) ? STATUS_SUCCESS : STATUS_PROCEDURE_NOT_FOUND;
182 }
183
184
185 /******************************************************************
186  *              LdrShutdownProcess (NTDLL.@)
187  *
188  */
189 NTSTATUS    WINAPI  LdrShutdownProcess(void)
190 {
191     TRACE("()\n");
192     MODULE_DllProcessDetach( TRUE, (LPVOID)1 );
193     return STATUS_SUCCESS; /* FIXME */
194 }
195
196 /******************************************************************
197  *              LdrShutdownThread (NTDLL.@)
198  *
199  */
200 NTSTATUS WINAPI LdrShutdownThread(void)
201 {
202     WINE_MODREF *wm;
203     TRACE("()\n");
204
205     /* don't do any detach calls if process is exiting */
206     if (process_detaching) return STATUS_SUCCESS;
207     /* FIXME: there is still a race here */
208
209     RtlEnterCriticalSection( &loader_section );
210
211     for ( wm = MODULE_modref_list; wm; wm = wm->next )
212     {
213         if ( !(wm->flags & WINE_MODREF_PROCESS_ATTACHED) )
214             continue;
215         if ( wm->flags & WINE_MODREF_NO_DLL_CALLS )
216             continue;
217
218         MODULE_InitDLL( wm, DLL_THREAD_DETACH, NULL );
219     }
220
221     RtlLeaveCriticalSection( &loader_section );
222     return STATUS_SUCCESS; /* FIXME */
223 }
224
225 /***********************************************************************
226  *           MODULE_FlushModrefs
227  *
228  * NOTE: Assumes that the process critical section is held!
229  *
230  * Remove all unused modrefs and call the internal unloading routines
231  * for the library type.
232  */
233 static void MODULE_FlushModrefs(void)
234 {
235     WINE_MODREF *wm, *next;
236
237     for (wm = MODULE_modref_list; wm; wm = next)
238     {
239         next = wm->next;
240
241         if (wm->refCount)
242             continue;
243
244         /* Unlink this modref from the chain */
245         if (wm->next)
246             wm->next->prev = wm->prev;
247         if (wm->prev)
248             wm->prev->next = wm->next;
249         if (wm == MODULE_modref_list)
250             MODULE_modref_list = wm->next;
251
252         TRACE(" unloading %s\n", wm->filename);
253         if (!TRACE_ON(module))
254             TRACE_(loaddll)("Unloaded module '%s' : %s\n", wm->filename,
255                             wm->dlhandle ? "builtin" : "native" );
256
257         SERVER_START_REQ( unload_dll )
258         {
259             req->base = (void *)wm->module;
260             wine_server_call( req );
261         }
262         SERVER_END_REQ;
263
264         if (wm->dlhandle) wine_dll_unload( wm->dlhandle );
265         else UnmapViewOfFile( (LPVOID)wm->module );
266         FreeLibrary16( wm->hDummyMod );
267         RtlFreeHeap( ntdll_get_process_heap(), 0, wm->deps );
268         RtlFreeHeap( ntdll_get_process_heap(), 0, wm );
269     }
270 }
271
272 /***********************************************************************
273  *           MODULE_DecRefCount
274  *
275  * NOTE: Assumes that the process critical section is held!
276  */
277 static void MODULE_DecRefCount( WINE_MODREF *wm )
278 {
279     int i;
280
281     if ( wm->flags & WINE_MODREF_MARKER )
282         return;
283
284     if ( wm->refCount <= 0 )
285         return;
286
287     --wm->refCount;
288     TRACE("(%s) refCount: %d\n", wm->modname, wm->refCount );
289
290     if ( wm->refCount == 0 )
291     {
292         wm->flags |= WINE_MODREF_MARKER;
293
294         for ( i = 0; i < wm->nDeps; i++ )
295             if ( wm->deps[i] )
296                 MODULE_DecRefCount( wm->deps[i] );
297
298         wm->flags &= ~WINE_MODREF_MARKER;
299     }
300 }
301
302 /******************************************************************
303  *              LdrUnloadDll (NTDLL.@)
304  *
305  *
306  */
307 NTSTATUS WINAPI LdrUnloadDll( HMODULE hModule )
308 {
309     NTSTATUS retv = STATUS_SUCCESS;
310
311     TRACE("(%p)\n", hModule);
312
313     RtlEnterCriticalSection( &loader_section );
314
315     /* if we're stopping the whole process (and forcing the removal of all
316      * DLLs) the library will be freed anyway
317      */
318     if (!process_detaching)
319     {
320         WINE_MODREF *wm;
321
322         free_lib_count++;
323         if ((wm = MODULE32_LookupHMODULE( hModule )) != NULL)
324         {
325             TRACE("(%s) - START\n", wm->modname);
326
327             /* Recursively decrement reference counts */
328             MODULE_DecRefCount( wm );
329
330             /* Call process detach notifications */
331             if ( free_lib_count <= 1 )
332             {
333                 MODULE_DllProcessDetach( FALSE, NULL );
334                 MODULE_FlushModrefs();
335             }
336
337             TRACE("END\n");
338         }
339         else
340             retv = STATUS_DLL_NOT_FOUND;
341
342         free_lib_count--;
343     }
344
345     RtlLeaveCriticalSection( &loader_section );
346
347     return retv;
348 }
349
350 /***********************************************************************
351  *           RtlImageNtHeader   (NTDLL.@)
352  */
353 PIMAGE_NT_HEADERS WINAPI RtlImageNtHeader(HMODULE hModule)
354 {
355     IMAGE_NT_HEADERS *ret;
356
357     __TRY
358     {
359         IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)hModule;
360
361         ret = NULL;
362         if (dos->e_magic == IMAGE_DOS_SIGNATURE)
363         {
364             ret = (IMAGE_NT_HEADERS *)((char *)dos + dos->e_lfanew);
365             if (ret->Signature != IMAGE_NT_SIGNATURE) ret = NULL;
366         }
367     }
368     __EXCEPT(page_fault)
369     {
370         return NULL;
371     }
372     __ENDTRY
373     return ret;
374 }
375
376
377 /***********************************************************************
378  *           RtlImageDirectoryEntryToData   (NTDLL.@)
379  */
380 PVOID WINAPI RtlImageDirectoryEntryToData( HMODULE module, BOOL image, WORD dir, ULONG *size )
381 {
382     const IMAGE_NT_HEADERS *nt;
383     DWORD addr;
384
385     if ((ULONG_PTR)module & 1)  /* mapped as data file */
386     {
387         module = (HMODULE)((ULONG_PTR)module & ~1);
388         image = FALSE;
389     }
390     if (!(nt = RtlImageNtHeader( module ))) return NULL;
391     if (dir >= nt->OptionalHeader.NumberOfRvaAndSizes) return NULL;
392     if (!(addr = nt->OptionalHeader.DataDirectory[dir].VirtualAddress)) return NULL;
393     *size = nt->OptionalHeader.DataDirectory[dir].Size;
394     if (image || addr < nt->OptionalHeader.SizeOfHeaders) return (char *)module + addr;
395
396     /* not mapped as image, need to find the section containing the virtual address */
397     return RtlImageRvaToVa( nt, module, addr, NULL );
398 }
399
400
401 /***********************************************************************
402  *           RtlImageRvaToSection   (NTDLL.@)
403  */
404 PIMAGE_SECTION_HEADER WINAPI RtlImageRvaToSection( const IMAGE_NT_HEADERS *nt,
405                                                    HMODULE module, DWORD rva )
406 {
407     int i;
408     IMAGE_SECTION_HEADER *sec = (IMAGE_SECTION_HEADER*)((char*)&nt->OptionalHeader +
409                                                         nt->FileHeader.SizeOfOptionalHeader);
410     for (i = 0; i < nt->FileHeader.NumberOfSections; i++, sec++)
411     {
412         if ((sec->VirtualAddress <= rva) && (sec->VirtualAddress + sec->SizeOfRawData > rva))
413             return sec;
414     }
415     return NULL;
416 }
417
418
419 /***********************************************************************
420  *           RtlImageRvaToVa   (NTDLL.@)
421  */
422 PVOID WINAPI RtlImageRvaToVa( const IMAGE_NT_HEADERS *nt, HMODULE module,
423                               DWORD rva, IMAGE_SECTION_HEADER **section )
424 {
425     IMAGE_SECTION_HEADER *sec;
426
427     if (section && *section)  /* try this section first */
428     {
429         sec = *section;
430         if ((sec->VirtualAddress <= rva) && (sec->VirtualAddress + sec->SizeOfRawData > rva))
431             goto found;
432     }
433     if (!(sec = RtlImageRvaToSection( nt, module, rva ))) return NULL;
434  found:
435     if (section) *section = sec;
436     return (char *)module + sec->PointerToRawData + (rva - sec->VirtualAddress);
437 }