OSDN Git Service

Represent <winbase.h> file creation bit-flags in hexadecimal.
[mingw/mingw-org-wsl.git] / mingwrt / mingwex / dlfcn.c
1 /*
2  * dlfcn.c
3  *
4  * Core implementation for (approximately) POSIX conforming dlopen(),
5  * dlsym(), dlclose(), and dlerror() API functions.
6  *
7  * $Id$
8  *
9  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
10  * Copyright (C) 2014, MinGW.org Project
11  *
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice, this permission notice, and the following
21  * disclaimer shall be included in all copies or substantial portions of
22  * the Software.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
27  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR OTHER
30  * DEALINGS IN THE SOFTWARE.
31  *
32  *
33  * This source file implements the core functionality for each of the POSIX
34  * dynamic loader API functions, dlopen(), dlsym(), dlclose(), and dlerror();
35  * each is implemented such that it is conveniently accessed via a macro, or
36  * an inline function representation, by way of a publicly visible table of
37  * entry point vectors (i.e. function pointers).
38  *
39  */
40 #include <dlfcn.h>
41 #include <stdio.h>
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <stdint.h>
45 #include <limits.h>
46
47 /* In addition to normal C runtime services, this implementation requires
48  * the use of the MS-Windows API; the light-weight subset will suffice...
49  */
50 #define WIN32_LEAN_AND_MEAN
51 /*
52  * ...while accessing it throught its standard interface.
53  */
54 #include <windows.h>
55
56 /* In addition to the POSIX constants which are defined in dlfcn.h,
57  * we also define some private manifest constants, which POSIX does
58  * not specify, but which facilitate our implementation:
59  *
60  *   RTLD_EXPLICIT, used to qualify RTLD_GLOBAL modules which have
61  *   been explicitly loaded by dlopen(), so an RTLD_DEFAULT search
62  *   need not consider them.
63  */
64 # define RTLD_EXPLICIT     16
65 /*
66  *   RTLD_ALL_GLOBAL, returned by dlopen() when the module name is
67  *   given as a NULL pointer; this is a fake module handle, similar
68  *   to RTLD_DEFAULT and RTLD_NEXT, representing the corpus of all
69  *   RTLD_GLOBAL modules, both implicitly and explicitly loaded.
70  */
71 # define RTLD_ALL_GLOBAL  (void *)(-5)
72
73 /* Before anything else, ensure that the dlerror() implementation
74  * is in place, so that other components may access it freely.
75  *
76  * We provide TWO reference pointers for error message buffering, so
77  * that we may continue to hold a reference to allocated memory, even
78  * after dlerror() has discarded the pointer to a pending message;
79  * initially, we mark both as unassigned.
80  */
81 static char *dlfcn_error_pending = NULL;
82 static char *dlfcn_error_message = NULL;
83
84 static void dlfcn_store_error_message( const char *fmt, ... )
85 {
86   /* This private function provides printf() style formatting for
87    * dlfcn error messages, storing them into dynamically allocated
88    * memory, and updating both reference pointers for subsequent
89    * retrieval by the dlerror() accessor function.
90    */
91   int msglen;
92   va_list argv;
93   va_start( argv, fmt );
94   msglen = 1 + vsnprintf( NULL, 0, fmt, argv );
95   if( (dlfcn_error_pending = realloc( dlfcn_error_message, msglen )) != NULL )
96     /*
97      * Store message, only if a buffer was successfully allocated.
98      */
99     vsnprintf( dlfcn_error_pending, msglen, fmt, argv );
100   dlfcn_error_message = dlfcn_error_pending;
101   va_end( argv );
102 }
103
104 static char *dlfcn_strerror( int errcode )
105 {
106   /* This private function emulates strerror(), but substitutes an
107    * errcode obtained from GetLastError() for errno.
108    */
109   char *text;
110   uint32_t description = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
111       | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
112       errcode, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (char *)(&text),
113       0, NULL
114     );
115   if( description )
116   {
117     /* We successfully obtained an error description; POSIX insists
118      * that it must not be encoded with any terminating newline.
119      */
120     char *tmp = text + strlen( text );
121     while( (*--tmp == '\n') || (*tmp == '\r') ) *tmp = '\0';
122
123     /* Ensure that we consistently return the error description in
124      * a buffer which may be released by the free() function, (since
125      * we cannot be sure that LocalFree() is compatible).
126      */
127     text = strdup( tmp = text );
128     LocalFree( tmp );
129   }
130   else
131   { /* We were unable to find an error description; substitute a
132      * formatted reference to the unknown error code.
133      */
134     char *fmt = "Unknown error %d";
135     char tmp[1 + snprintf( NULL, 0, fmt, errcode )];
136     snprintf( tmp, sizeof( tmp ), fmt, errcode );
137     text = strdup( tmp );
138   }
139   /* However we derived it, the error description is now available
140    * in a dynamically allocated text buffer which may be released
141    * by calling free().
142    */
143   return text;
144 }
145
146 static char *dlerror_internal( void )
147 {
148   /* This is the internal implementation of the public dlerror() API.
149    * POSIX does not require this to be thread safe, so we take no care
150    * to make it such.
151    */
152   if( (dlfcn_error_pending == NULL) && (dlfcn_error_message != NULL) )
153   {
154     /* There is no pending message, but a buffer remains allocated
155      * to one which has already been retrieved; we may release it.
156      */
157     free( dlfcn_error_message );
158     dlfcn_error_message = NULL;
159   }
160   /* Mark any pending error message as "retrieved"...
161    */
162   dlfcn_error_pending = NULL;
163   /*
164    * ...and return it.
165    */
166   return dlfcn_error_message;
167 }
168
169 typedef
170 /* A pointer type, representing a reference to a function for
171  * retrieving exported symbol references from any loaded module.
172  */
173 int WINAPI (*lookup_fn)( HANDLE, HMODULE *, uint32_t, uint32_t * );
174
175 /* POSIX requires that dlsym() be able to look up symbol references
176  * for any dynamically loaded module within the address space of the
177  * calling process, including those which have been implicitly loaded
178  * at process start-up.  To facilitate this, we will use Microsoft's
179  * PSAPI.DLL services, to enumerate such implicitly loaded modules,
180  * before we explicitly load any using dlopen(), or we perform any
181  * look-up using dlsym() itself.
182  */
183 static struct
184 { /* Structure providing a convenient encapsulation of the PSAPI.DLL
185    * interface, which may itself need to be explicitly loaded.
186    */
187   HMODULE        dll;
188   const char    *dll_name;
189   const char    *lookup_fn_name;
190   lookup_fn      lookup;
191 }
192 psapi = { NULL, "psapi.dll", "EnumProcessModules", NULL };
193
194 /* The two NULL fields, within the preceding structure, must be
195  * initialized before use; the following pair of inline functions
196  * facilitate this, with the first looking up the API entry...
197  */
198 static __inline__ lookup_fn psapi_lookup_fn( HMODULE provider )
199 { return (lookup_fn)(GetProcAddress( provider, psapi.lookup_fn_name )); }
200
201 /* ...when called from within the second, which both ensures that
202  * PSAPI.DLL is loaded, and assigns the requisite field values.
203  */
204 static __inline__ lookup_fn psapi_lookup_fn_init( void )
205 { return psapi.lookup = ((psapi.dll = LoadLibrary( psapi.dll_name )) != NULL)
206     ? psapi_lookup_fn( psapi.dll ) : NULL;
207 }
208
209 /* After the PSAPI.DLL interface has been initialized, by calling
210  * the preceding function, we may call the following inline helper
211  * to retrieve a list of module handles...
212  */
213 static __inline__ unsigned int psapi_enum_modules( HMODULE **modules )
214 {
215   /* ...focussing on modules which are currently loaded within
216    * the address space of the current process.
217    */
218   HANDLE me = GetCurrentProcess();
219
220   /* We will dynamically allocate memory to store the handles;
221    * initially we request zero bytes of storage to receive them,
222    * then we use a while loop, which should execute exactly twice,
223    * to retrieve them; on the first pass, we determine the actual
224    * memory requirement and allocate it; on the second, we store
225    * the list of module handles into it...
226    */
227   uint32_t wanted, request = 0;
228   while( psapi.lookup( me, *modules, request, &wanted ) && (wanted > request) )
229     if( (*modules = realloc( *modules, request = wanted )) == NULL )
230     {
231       /* ...trying to record an appropriate diagnostic message on
232        * failure, (but noting that failure is likely to result from
233        * insufficient memory, so there may not be enough to record
234        * the message either)...
235        */
236       char *reason = dlfcn_strerror( ERROR_OUTOFMEMORY );
237       dlfcn_store_error_message( "dlfcn_init:enum_modules: %s", reason );
238       free( reason );
239     }
240   /* ...before ultimately returning the number retrieved.
241    */
242   return wanted / sizeof( HMODULE );
243 }
244
245 typedef struct dltab
246 {
247   /* Structure used to map module handle references, and associated
248    * status, into the global modules list.
249    */
250   unsigned int   slots;
251   unsigned char *flags;
252   HMODULE       *modules;
253 } dltab;
254
255 /* The global modules list itself, initially empty, but suitable
256  * for reallocation on the heap.
257  */
258 static dltab rtld = { 0, NULL, NULL };
259
260 /* Microsoft's LoadLibrary() API is explicitly documented as being
261  * unable to handle regular slashes as directory separators in module
262  * path names, (in spite of their validity elsewhere), thus...
263  */
264 static __inline__ wchar_t *normalized_form( wchar_t *pathname )
265 {
266   /* ...we provide this helper, to replace them with backslashes.
267    */
268   wchar_t *scan = pathname;
269   do { if( *scan == L'/' ) *scan = L'\\'; } while( *scan++ != L'\0' );
270   return pathname;
271 }
272
273 static void dlopen_store_error_message( const char *name, unsigned status )
274 {
275   /* A convenience helper, to record diagnostic messages to explain
276    * causes of failure encountered when calling dlopen().
277    */
278   char *reason = dlfcn_strerror( status );
279   dlfcn_store_error_message( "dlopen:'%s': %s", name, reason );
280   free( reason );
281 }
282
283 static void *dlopen_internal( const char *name, int mode )
284 {
285   /* This is the formal implementation of the public dlopen() function;
286    * note that this (currently) ignores the RTLD_LAZY loading option, and
287    * processes each request as if specified as RTLD_NOW.
288    */
289   if( name == NULL )
290     /* POSIX specifies this as a special case, requiring us to return
291      * a handle, through which all symbols exported by all modules which
292      * are currently loaded with the RTLD_GLOBAL attribute, (explicitly
293      * or implicitly), would be accessible.
294      */
295     return RTLD_ALL_GLOBAL;
296
297   /* We will use LoadLibrary() to obtain a handle for the dlopen()ed
298    * module; Microsoft advise us that we should ensure that we always
299    * use backslashes, and to avoid use of POSIX compatible slashes, in
300    * any path name passed to this function.  To ensure this, the name
301    * argument should be normalized, (working in the UTF-16LE domain,
302    * to avoid inadvertent transformation of trail bytes within MBC
303    * sequences), to transform slashes accordingly.
304    */
305   size_t buflen; wchar_t internal_name[buflen = 1 + strlen( name )];
306   MultiByteToWideChar( CP_ACP, 0, name, -1, internal_name, buflen );
307
308   /* Now, we may safely call LoadLibrary(), to obtain a module handle.
309    */
310   void *module = LoadLibraryW( normalized_form( internal_name ) );
311   if( module == NULL )
312   {
313     /* The named module could not be opened; record an appropriate
314      * error message for retrieval by dlerror().
315      */
316     dlopen_store_error_message( name, GetLastError() );
317   }
318   else
319   { /* We got a handle for the requested module; we need to ensure
320      * that it is allocated a slot in our global symbol table, but we
321      * must first check that it isn't already present.
322      */
323     int index, insertion_point = rtld.slots;
324     for( index = 0; index < rtld.slots; index++ )
325     {
326       /* As we scan the list of already loaded modules, check for any
327        * existing slot which may be vacant...
328        */
329       if( rtld.flags[index] == 0 )
330       {
331         /* ...marking the first available, if any, as a candidate for
332          * possible insertion of a new entry.
333          */
334         if( index < insertion_point )
335           insertion_point = index;
336       }
337       else if( module == rtld.modules[index] )
338       {
339         /* The requested module appears to be loaded already; calling
340          * LoadLibrary() will have increased its reference count, but
341          * our management strategy doesn't require this; reduce it...
342          */
343         FreeLibrary( module );
344         /*
345          * ...but promote its existing status to RTLD_GLOBAL, if that
346          * is indicated as required by the requested mode...
347          */
348         rtld.flags[index] |= mode & RTLD_GLOBAL;
349         /*
350          * ...and immediately return the module handle.
351          */
352         return module;
353       }
354     }
355     /* If we get to here, there is no entry for the requested module,
356      * within the global modules list; we must add it now, either using
357      * an existing vacant slot, if there is one...
358      */
359     if( insertion_point < rtld.slots )
360     {
361       /* ...but noting that we cannot simply insert a new reference
362        * within it, since that would disrupt the order in which modules
363        * are subsequently searched, and POSIX requires that this is to
364        * preserve loading order, (strictly, symbol resolution order),
365        * when searching RTLD_GLOBAL modules, (and since any module may
366        * be promoted to RTLD_GLOBAL status, even after it was originally
367        * loaded as RTLD_LOCAL, this means that we must preserve loading
368        * order for ALL active modules).  Thus, we must pack the list of
369        * active modules after the nominated insertion point...
370        */
371       index = insertion_point;
372
373       /* ...after which, the first vacant slot will have been relocated
374        * to follow all active slots, and we may adjust the nominated
375        * insertion point accordingly.
376        */
377       do { /* First, we identify the first slot following the nominated
378             * insertion point, which is NOT vacant.
379             */
380            while( (index < rtld.slots) && (rtld.flags[index] == 0) )
381              ++index;
382
383            /* Now, we move that non-vacant slot, and any which follow
384             * it, upwards in the list, to fill vacant slots...
385             */
386            while( (index < rtld.slots) && (rtld.flags[index] != 0) )
387            {
388              /* ...by simply copying content from the non-vacant slots
389               * to overwrite content in the preceding slots...
390               */
391              rtld.modules[insertion_point] = rtld.modules[index];
392              rtld.flags[insertion_point++] = rtld.flags[index++];
393            }
394            /* ...repeating the entire procedure, until all vacant slots
395             * have been filled, and the nominated insertion point has
396             * been moved to follow the last relocated non-vacant entry.
397             */
398          } while( index < rtld.slots );
399
400       /* After packing, any already allocated slots after and including
401        * the relocated insertion point MUST be vacant; ensure that they
402        * are marked accordingly.
403        */
404       for( index = insertion_point; index < rtld.slots; index++ )
405         rtld.flags[index] = 0;
406     }
407     else
408     { /* There is no vacant slot: we must expand the allocated memory
409        * pool to create one; first increment the modules list size...
410        */
411       size_t slots = 1 + rtld.slots;
412       HMODULE *modules = rtld.modules;
413       if( (modules = realloc( modules, sizeof( HMODULE ) * slots )) != NULL )
414       {
415         /* ...and, having sucessfully increased the modules list memory
416          * allocation, do likewise for the associated flags...
417          */
418         unsigned char *flags = rtld.flags;
419         if( (flags = realloc( flags, slots )) != NULL )
420           /*
421            * ...initializing the new flags register, and incrementing
422            * the slots count, when that is also successful.
423            */
424           (rtld.flags = flags)[rtld.slots++] = 0;
425
426         /* Regardless of the success, or otherwise, of the flags memory
427          * adjustment, the modules list was reallocated, so we need to
428          * adjust its reference pointer accordingly.
429          */
430         rtld.modules = modules;
431       }
432       /* Before proceeding further, verify that the new slot has been
433        * fully created, and is ready to store the module data...
434        */
435       if( insertion_point == rtld.slots )
436       {
437         /* ...but if allocation failed, the nominated insertion point
438          * will lie beyond the available space, so we MUST fail, after
439          * discarding the now unreferenced module handle, and trying to
440          * record an appropriate diagnostic message, (but we note that
441          * this may also fail, due to insufficient memory).
442          */
443         FreeLibrary( module );
444         dlopen_store_error_message( name, ERROR_OUTOFMEMORY );
445         return NULL;
446       }
447     }
448     /* When we get to here, we now have a suitable slot in which to add
449      * the reference data for the newly dlopen()ed module, (either by
450      * allocation of a new slot, or by relocation of an existing vacant
451      * slot); we may safely store the appropriate reference data.
452      */
453     rtld.flags[insertion_point] = RTLD_EXPLICIT | mode;
454     rtld.modules[insertion_point] = module;
455   }
456   /* Finally, we return whatever module handle we got from LoadLibrary(),
457    * (which may be NULL, if this failed).
458    */
459   return module;
460 }
461
462 static void *dlsym_internal( void *module, const char *name )
463 {
464   /* This is the formal implementation of the public dlsym() function.
465    */
466   static unsigned int index = 0;
467   static const char *last_named_symbol = NULL;
468   static unsigned char rtld_exclude = 0;
469   void *rtn;
470
471   if( module == RTLD_NEXT )
472   {
473     /* NOTE: We MUST identify this special case BEFORE any other!
474      *
475      * POSIX doesn't require this to be supported yet, but reserves it
476      * for future use; it should cause dlsym() to repeat its search for
477      * the named symbol, (provided it remains the same as last named in
478      * a global search), continuing from the next module in the current
479      * search order, following that in which the symbol was previously
480      * found; (this permits us to locate symbols which may defined in
481      * more than one loaded module).
482      */
483     index = (name == last_named_symbol) ? index + 1 : 0;
484     module = RTLD_ALL_GLOBAL;
485   }
486   else if( module == RTLD_DEFAULT )
487   {
488     /* NOTE: We MUST keep this AFTER the check for RTLD_NEXT!
489      *
490      * Once again, POSIX doesn't require this to be supported yet, but
491      * reserves it for future use to search for symbols which could have
492      * been found within the process address space prior to any explicit
493      * dlopen() call; this capability may be supported by searching all
494      * modules in the address space, (i.e. equivalent to a search with
495      * module == RTLD_ALL_GLOBAL), excluding those which have been
496      * explicitly loaded since process start-up.
497      */
498     module = RTLD_ALL_GLOBAL;
499     rtld_exclude = RTLD_EXPLICIT;
500     index = 0;
501   }
502   else
503     /* Neither RTLD_DEFAULT, nor RTLD_NEXT was specified; we must reset
504      * the RTLD_GLOBAL search index, and cancel all search exclusions.
505      */
506     index = rtld_exclude = 0;
507
508   if( module == RTLD_ALL_GLOBAL )
509   {
510     /* The RTLD_ALL_GLOBAL module reference represents a request to
511      * perform an in-order traversal of all modules within the process
512      * address space, either implicitly loaded, or explicitly loaded
513      * with the RTLD_GLOBAL mode attribute, either until the named
514      * symbol is found, or all such modules have been searched
515      * without finding it.
516      */ 
517     for( rtn = NULL; (rtn == NULL) && (index < rtld.slots); index++ )
518       if( ((rtld_exclude & rtld.flags[index]) == 0)
519       &&  ((rtld.flags[index] & RTLD_GLOBAL) == RTLD_GLOBAL)  )
520         rtn = GetProcAddress( rtld.modules[index], name );
521
522     /* Note the symbol named in the current search, so that we may
523      * check for consistency in the event that the next search is
524      * invoked as an RTLD_NEXT request.
525      */
526     last_named_symbol = name;
527   }
528   else
529   { /* When a specific module reference is specified, confine the
530      * search to the specified module alone...
531      */
532     rtn = GetProcAddress( (HMODULE)(module), name );
533
534     /* ...and inhibit any attempt to follow this search with one
535      * specifying RTLD_NEXT; (this would not be valid, since there
536      * is no concept of a "next" module to be searched, when not
537      * searching through an ordered list of modules).
538      */
539     last_named_symbol = NULL;
540   }
541
542   if( rtn == NULL )
543   {
544     /* The named symbol was not found in any module which was searched;
545      * record the appropriate error message for retrieval by dlerror().
546      */
547     char *reason = dlfcn_strerror( GetLastError() );
548     dlfcn_store_error_message( "dlsym:'%s': %s", name, reason );
549     free( reason );
550   }
551
552   /* Return the symbol address, as assigned to the return value;
553    * (this will be NULL, if the named symbol was not found).
554    */
555   return rtn;
556 }
557
558 static int dlclose_store_error_message( int status )
559 {
560   /* A private helper function to record an appropriate dlerror()
561    * message, on failure of dlclose().
562    */
563   char *reason = dlfcn_strerror( status = GetLastError() );
564   dlfcn_store_error_message( "dlclose: %s", reason );
565   free( reason );
566   return status;
567 }
568
569 static __inline__ int dlclose_internal_check_for_error( int status )
570 {
571   /* A private helper function to set the return status for dlclose(),
572    * while also recording a dlerror() message, when status is "failed".
573    */
574   return (status == 0) ? dlclose_store_error_message( status ) : 0;
575 }
576
577 static int dlclose_internal( void *module )
578 {
579   /* This is the formal implementation of the public dlclose() function;
580    * it will call Microsoft's FreeLibrary() function passing the specified
581    * module handle, provided this is listed in the global module table as
582    * having been explicitly opened by our dlopen() function.
583    */
584   int index;
585   for( index = 0; index < rtld.slots; index++ )
586     if( module == rtld.modules[index] )
587     {
588       /* The specified module handle is present in the global modules list;
589        * while we could simply call FreeLibrary() immediately, it may not be
590        * prudent to do so in respect of implicitly loaded modules, but for
591        * those which we have explicitly loaded...
592        */
593       if( ((rtld.flags[index] & RTLD_EXPLICIT) == RTLD_EXPLICIT)
594       /*
595        * ...and which can be successfully released by FreeLibrary()...
596        */
597       &&  (dlclose_internal_check_for_error( FreeLibrary( module )) == 0)  )
598         /*
599          * ...we mark them as no longer available for dlsym() processing,
600          * and return immediately, indicating success...
601          */
602         return rtld.flags[index] = 0;
603
604       /* ...but when we didn't successfully release the module, we have
605        * no need to continue the search for its handle in the global list
606        * of modules, (because we've already found it); we may immediately
607        * abandon the search.
608        */
609       break;
610     }
611
612   /* If we get to here, we either didn't find the specified module handle in
613    * the global list of modules, or we found it but were unable to release
614    * it; in either case, we force a module error condition.
615    */
616   return dlclose_store_error_message( FreeLibrary( NULL ) );
617 }
618
619 static void dlfcn_init( void )
620 {
621   /* This private initialization function must be called, as a
622    * prerequisite to the first use of either dlopen() or dlsym()
623    * in any process; it uses Microsoft's PSAPI.DLL interface to
624    * enumerate the implicitly loaded process modules, so that
625    * they may be searched implicitly by dlsym().
626    */
627   if( psapi_lookup_fn_init() == NULL )
628   {
629     /* Initialization of the PSAPI.DLL interface failed.  Record
630      * this for possible retrieval by dlerror(); we can do no more,
631      * so the list of implicitly loaded mudules will remain empty.
632      */
633     char *reason = dlfcn_strerror( GetLastError() );
634     dlfcn_store_error_message( "dlfcn_init:%s: %s", psapi.dll_name, reason );
635     free( reason );
636   }
637   else
638   { /* We've acquired a reference for the PSAPI EnumProcessModules()
639      * service; invoke it, to obtain a list of all modules which are
640      * currently accessible, within the address space of the current
641      * process, (which we assume to represent all modules which were
642      * loaded implicitly, at process start-up)...
643      */
644     rtld.slots = psapi_enum_modules( &(rtld.modules) );
645     /*
646      * ...but, noting that we explicitly added a refererence for the
647      * PSAPI.DLL services interface, which we needed to acquire this
648      * list, we may now discard that reference.
649      */
650     FreeLibrary( psapi.dll );
651
652     /* Provided we've obtained a non-empty list of modules...
653      */
654     if(  (rtld.modules != NULL)
655     /*
656      * ...and we can successfully allocate storage for an associated
657      * collection of flags registers, (one per module)...
658      */
659     &&  ((rtld.flags = realloc( rtld.flags, rtld.slots )) != NULL)  )
660     {
661       /* ...examine each reference in the modules list, and for each
662        * which is not NULL...
663        */
664       unsigned int index;
665       for( index = 0; index < rtld.slots; index++ )
666         if( (rtld.modules[index] != NULL)
667         /*
668          * ...and which is not the PSAPI.DLL reference we've already
669          * released...
670          */
671         &&  ((rtld.modules[index] != psapi.dll)
672         /*
673          * ...unless it continues to provide a valid reference to the
674          * PSAPI EnumProcessModules() service, (which indicates that
675          * the associated DLL had been implicitly loaded, BEFORE we
676          * explicitly requested it)...
677          */
678         ||  (psapi_lookup_fn( psapi.dll ) == psapi.lookup))  )
679           /*
680            * ...then we set the associated flags to indicate that the
681            * module was implicitly loaded and globally accessible...
682            */
683           rtld.flags[index] = RTLD_GLOBAL;
684         else
685           /* ...otherwise, we mark it as if dlclose() has already been
686            * implicitly called, to discard it.
687            */
688           rtld.flags[index] = 0;
689
690       /* Having thus populated the global modules list, on first call
691        * to either dlopen() or dlsym(), we don't need to (and indeed we
692        * shouldn't) do this again; redirect each of these two calls to
693        * their respective internal handlers.
694        */
695       __mingw_dlfcn.dlopen = dlopen_internal;
696       __mingw_dlfcn.dlsym = dlsym_internal;
697     }
698   }
699 }
700
701 /* The global symbol table needs to be initialized, before we process
702  * the first call to either dlopen() or dlsym(); the following pair of
703  * initializer functions take care of this requirement, before passing
704  * the first-time request to the appropriate internal handler.
705  */
706 static void *dlopen_init( const char *name, int mode )
707 { dlfcn_init(); return dlopen_internal( name, mode ); }
708
709 static void *dlsym_init( void *module, const char *name )
710 { dlfcn_init(); return dlsym_internal( module, name ); }
711
712 /* Finally, we may define the __mingw_dlfcn structure, and set up its
713  * initial function pointers, referring to the four API functions...
714  */
715 struct __dlfcn__ __mingw_dlfcn =
716 { dlopen_init,          /* dlopen() implementation, with initialization. */
717   dlsym_init,           /* dlsym() implementation, with initialization.  */
718   dlerror_internal,     /* dlerror() implementation; (direct reference). */
719   dlclose_internal      /* dlclose() implementation; (direct reference). */
720 };
721
722 /* $RCSfile$: end of file */