3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
18 #include "perprocess.h"
22 #include "shared_info.h"
24 #include "pathfinder.h"
26 /* Dumb allocator using memory from tmp_pathbuf.w_get ().
28 Does not reuse free'd memory areas. Instead, memory
29 is released when the tmp_pathbuf goes out of scope.
31 ATTENTION: Requesting memory from an instance of tmp_pathbuf breaks
32 when another instance on a newer stack frame has provided memory. */
33 class tmp_pathbuf_allocator
34 : public allocator_interface
45 /* allocator_interface */
46 virtual void * alloc (size_t need
)
49 need
= 1; /* GNU-ish */
50 size_t chunksize
= NT_MAX_PATH
* sizeof (WCHAR
);
52 api_fatal ("temporary buffer too small for %d bytes", need
);
55 /* skip remaining, use next chunk */
56 freemem_
.wideptr
= tp_
.w_get ();
57 freesize_
= chunksize
;
60 void * ret
= freemem_
.voidptr
;
62 /* adjust remaining, aligned at 8 byte boundary */
63 need
= need
+ 7 - (need
- 1) % 8;
64 freemem_
.byteptr
+= need
;
73 /* allocator_interface */
74 virtual void free (void *)
76 /* no-op: released by tmp_pathbuf at end of scope */
79 tmp_pathbuf_allocator ();
80 tmp_pathbuf_allocator (tmp_pathbuf_allocator
const &);
81 tmp_pathbuf_allocator
& operator = (tmp_pathbuf_allocator
const &);
84 /* use tmp_pathbuf of current stack frame */
85 tmp_pathbuf_allocator (tmp_pathbuf
& tp
)
86 : allocator_interface ()
94 set_dl_error (const char *str
)
96 strcpy (_my_tls
.locals
.dl_buffer
, strerror (get_errno ()));
97 _my_tls
.locals
.dl_error
= 1;
100 /* Identify basename and baselen within name,
101 return true if there is a dir in name. */
103 spot_basename (const char * &basename
, int &baselen
, const char * name
)
105 basename
= strrchr (name
, '/');
106 basename
= basename
? basename
+ 1 : name
;
107 baselen
= name
+ strlen (name
) - basename
;
108 return basename
> name
;
111 /* Setup basenames using basename and baselen,
112 return true if basenames do have some suffix. */
114 collect_basenames (pathfinder::basenamelist
& basenames
,
115 const char * basename
, int baselen
)
117 /* like strrchr (basename, '.'), but limited to baselen */
118 const char *suffix
= basename
+ baselen
;
119 while (--suffix
>= basename
)
124 if (suffix
>= basename
)
125 suffixlen
= basename
+ baselen
- suffix
;
132 char const * ext
= "";
133 /* Without some suffix, reserve space for a trailing dot to override
134 GetModuleHandleExA's automatic adding of the ".dll" suffix. */
135 int extlen
= suffix
? 0 : 1;
137 /* If we have the ".so" suffix, ... */
138 if (suffixlen
== 3 && !strncmp (suffix
, ".so", 3))
140 /* ... keep the basename with original suffix, before ... */
141 basenames
.appendv (basename
, baselen
, NULL
);
142 /* ... replacing the ".so" with the ".dll" suffix. */
147 /* If the basename starts with "lib", ... */
148 if (!strncmp (basename
, "lib", 3))
150 /* ... replace "lib" with "cyg", before ... */
151 basenames
.appendv ("cyg", 3, basename
+3, baselen
-3, ext
, extlen
, NULL
);
153 /* ... using original basename with new suffix. */
154 basenames
.appendv (basename
, baselen
, ext
, extlen
, NULL
);
157 /* Identify dir of current executable into exedirbuf using wpathbuf buffer.
158 Return length of exedirbuf on success, or zero on error. */
160 get_exedir (char * exedirbuf
, wchar_t * wpathbuf
)
162 /* Unless we have a special cygwin loader, there is no such thing like
163 DT_RUNPATH on Windows we can use to search for dlls, except for the
164 directory of the main executable. */
167 wchar_t * wlastsep
= wcpcpy (wpathbuf
, global_progname
);
168 /* like wcsrchr(L'\\'), but we know the wcslen already */
169 while (--wlastsep
> wpathbuf
)
170 if (*wlastsep
== L
'\\')
172 if (wlastsep
<= wpathbuf
)
176 if (mount_table
->conv_to_posix_path (wpathbuf
, exedirbuf
, 0))
179 return strlen (exedirbuf
);
183 dlopen (const char *name
, int flags
)
189 if (name
== NULL
|| *name
== '\0')
191 ret
= (void *) GetModuleHandle (NULL
); /* handle for the current module */
197 DWORD gmheflags
= (flags
& RTLD_NODELETE
)
198 ? GET_MODULE_HANDLE_EX_FLAG_PIN
201 tmp_pathbuf tp
; /* single one per stack frame */
202 tmp_pathbuf_allocator
allocator (tp
);
203 pathfinder::basenamelist
basenames (allocator
);
205 const char *basename
;
207 bool have_dir
= spot_basename (basename
, baselen
, name
);
208 collect_basenames (basenames
, basename
, baselen
);
210 /* handle for the named library */
211 path_conv real_filename
;
212 wchar_t *wpath
= tp
.w_get ();
213 char *cpath
= tp
.c_get ();
215 pathfinder
finder (allocator
, basenames
); /* eats basenames */
219 int dirlen
= basename
- 1 - name
;
221 /* if the specified dir is x/lib, and the current executable
222 dir is x/bin, do the /lib -> /bin mapping, which is the
223 same actually as adding the executable dir */
224 if (dirlen
>= 4 && !strncmp (name
+ dirlen
- 4, "/lib", 4))
226 int exedirlen
= get_exedir (cpath
, wpath
);
227 if (exedirlen
== dirlen
&&
228 !strncmp (cpath
, name
, dirlen
- 4) &&
229 !strcmp (cpath
+ dirlen
- 4, "/bin"))
230 finder
.add_searchdir (cpath
, exedirlen
);
233 /* search the specified dir */
234 finder
.add_searchdir (name
, dirlen
);
238 /* NOTE: The Windows loader (for linked dlls) does
239 not use the LD_LIBRARY_PATH environment variable. */
240 finder
.add_envsearchpath ("LD_LIBRARY_PATH");
242 /* Finally we better have some fallback. */
243 finder
.add_searchdir ("/usr/bin", 8);
244 finder
.add_searchdir ("/usr/lib", 8);
247 /* now search the file system */
248 if (!finder
.find (pathfinder::
249 exists_and_not_dir (real_filename
,
250 PC_SYM_FOLLOW
| PC_POSIX
)))
252 /* If nothing worked, create a relative path from the original
253 incoming filename and let LoadLibrary search for it using the
254 system default DLL search path. */
255 real_filename
.check (name
, PC_SYM_FOLLOW
| PC_NOFULL
| PC_NULLEMPTY
);
256 if (real_filename
.error
)
260 real_filename
.get_wide_win32_path (wpath
);
261 /* Check if the last path component contains a dot. If so,
262 leave the filename alone. Otherwise add a trailing dot
263 to override LoadLibrary's automatic adding of a ".dll" suffix. */
264 wchar_t *last_bs
= wcsrchr (wpath
, L
'\\') ?: wpath
;
265 if (last_bs
&& !wcschr (last_bs
, L
'.'))
266 wcscat (last_bs
, L
".");
268 if (flags
& RTLD_NOLOAD
)
270 GetModuleHandleExW (gmheflags
, wpath
, (HMODULE
*) &ret
);
275 ret
= (void *) LoadLibraryW (wpath
);
276 /* reference counting */
279 dll
*d
= dlls
.find (ret
);
284 if (ret
&& gmheflags
)
285 GetModuleHandleExW (gmheflags
, wpath
, (HMODULE
*) &ret
);
293 set_dl_error ("dlopen");
294 debug_printf ("ret %p", ret
);
300 dlsym (void *handle
, const char *name
)
304 if (handle
== RTLD_DEFAULT
)
305 { /* search all modules */
309 buf
= RtlCreateQueryDebugBuffer (0, FALSE
);
313 set_dl_error ("dlsym");
316 status
= RtlQueryProcessDebugInformation (GetCurrentProcessId (),
318 if (!NT_SUCCESS (status
))
319 __seterrno_from_nt_status (status
);
322 PDEBUG_MODULE_ARRAY mods
= (PDEBUG_MODULE_ARRAY
)
323 buf
->ModuleInformation
;
324 for (ULONG i
= 0; i
< mods
->Count
; ++i
)
326 GetProcAddress ((HMODULE
) mods
->Modules
[i
].Base
, name
)))
331 RtlDestroyQueryDebugBuffer (buf
);
335 ret
= (void *) GetProcAddress ((HMODULE
) handle
, name
);
340 set_dl_error ("dlsym");
341 debug_printf ("ret %p", ret
);
346 dlclose (void *handle
)
349 if (handle
!= GetModuleHandle (NULL
))
351 /* reference counting */
352 dll
*d
= dlls
.find (handle
);
353 if (!d
|| d
->count
<= 0)
361 if (!FreeLibrary ((HMODULE
) handle
))
369 set_dl_error ("dlclose");
377 if (!_my_tls
.locals
.dl_error
)
381 _my_tls
.locals
.dl_error
= 0;
382 res
= _my_tls
.locals
.dl_buffer
;
388 dladdr (const void *addr
, Dl_info
*info
)
391 BOOL ret
= GetModuleHandleEx (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
397 /* Module handle happens to be equal to it's base load address. */
398 info
->dli_fbase
= hModule
;
400 /* Get the module filename. This pathname may be in short-, long- or //?/
401 format, depending on how it was specified when loaded, but we assume this
402 is always an absolute pathname. */
403 WCHAR fname
[MAX_PATH
];
404 DWORD length
= GetModuleFileNameW (hModule
, fname
, MAX_PATH
);
405 if ((length
== 0) || (length
== MAX_PATH
))
408 /* Convert to a cygwin pathname */
409 ssize_t conv
= cygwin_conv_path (CCP_WIN_W_TO_POSIX
| CCP_ABSOLUTE
, fname
,
410 info
->dli_fname
, MAX_PATH
);
414 /* Always indicate no symbol matching addr could be found. */
415 info
->dli_sname
= NULL
;
416 info
->dli_saddr
= NULL
;