Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / dlfcn.cc
blobfb705247352e46fb8fe408937a6e766fa37760e2
1 /* dlfcn.cc
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
7 details. */
9 #include "winsup.h"
10 #include <stdlib.h>
11 #include <dlfcn.h>
12 #include <ctype.h>
13 #include <wctype.h>
14 #include "path.h"
15 #include "fhandler.h"
16 #include "dtable.h"
17 #include "cygheap.h"
18 #include "perprocess.h"
19 #include "cygtls.h"
20 #include "tls_pbuf.h"
21 #include "ntdll.h"
22 #include "shared_info.h"
23 #include "dll_init.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
36 tmp_pathbuf & tp_;
37 union
39 PWCHAR wideptr;
40 void * voidptr;
41 char * byteptr;
42 } freemem_;
43 size_t freesize_;
45 /* allocator_interface */
46 virtual void * alloc (size_t need)
48 if (need == 0)
49 need = 1; /* GNU-ish */
50 size_t chunksize = NT_MAX_PATH * sizeof (WCHAR);
51 if (need > chunksize)
52 api_fatal ("temporary buffer too small for %d bytes", need);
53 if (need > freesize_)
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;
65 if (need > freesize_)
66 freesize_ = 0;
67 else
68 freesize_ -= need;
70 return ret;
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 &);
83 public:
84 /* use tmp_pathbuf of current stack frame */
85 tmp_pathbuf_allocator (tmp_pathbuf & tp)
86 : allocator_interface ()
87 , tp_ (tp)
88 , freemem_ ()
89 , freesize_ (0)
93 static void
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. */
102 static bool
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. */
113 static void
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)
120 if (*suffix == '.')
121 break;
123 int suffixlen;
124 if (suffix >= basename)
125 suffixlen = basename + baselen - suffix;
126 else
128 suffixlen = 0;
129 suffix = NULL;
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. */
143 baselen -= 3;
144 ext = ".dll";
145 extlen = 4;
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. */
159 static int
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. */
165 *exedirbuf = '\0';
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'\\')
171 break;
172 if (wlastsep <= wpathbuf)
173 return 0;
174 *wlastsep = L'\0';
176 if (mount_table->conv_to_posix_path (wpathbuf, exedirbuf, 0))
177 return 0;
179 return strlen (exedirbuf);
182 extern "C" void *
183 dlopen (const char *name, int flags)
185 void *ret = NULL;
189 if (name == NULL || *name == '\0')
191 ret = (void *) GetModuleHandle (NULL); /* handle for the current module */
192 if (!ret)
193 __seterrno ();
194 break;
197 DWORD gmheflags = (flags & RTLD_NODELETE)
198 ? GET_MODULE_HANDLE_EX_FLAG_PIN
199 : 0;
201 tmp_pathbuf tp; /* single one per stack frame */
202 tmp_pathbuf_allocator allocator (tp);
203 pathfinder::basenamelist basenames (allocator);
205 const char *basename;
206 int baselen;
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 */
217 if (have_dir)
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);
236 else
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)
257 break;
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);
271 if (ret)
272 break;
275 ret = (void *) LoadLibraryW (wpath);
276 /* reference counting */
277 if (ret)
279 dll *d = dlls.find (ret);
280 if (d)
281 ++d->count;
284 if (ret && gmheflags)
285 GetModuleHandleExW (gmheflags, wpath, (HMODULE *) &ret);
287 if (!ret)
288 __seterrno ();
290 while (0);
292 if (!ret)
293 set_dl_error ("dlopen");
294 debug_printf ("ret %p", ret);
296 return ret;
299 extern "C" void *
300 dlsym (void *handle, const char *name)
302 void *ret = NULL;
304 if (handle == RTLD_DEFAULT)
305 { /* search all modules */
306 PDEBUG_BUFFER buf;
307 NTSTATUS status;
309 buf = RtlCreateQueryDebugBuffer (0, FALSE);
310 if (!buf)
312 set_errno (ENOMEM);
313 set_dl_error ("dlsym");
314 return NULL;
316 status = RtlQueryProcessDebugInformation (GetCurrentProcessId (),
317 PDI_MODULES, buf);
318 if (!NT_SUCCESS (status))
319 __seterrno_from_nt_status (status);
320 else
322 PDEBUG_MODULE_ARRAY mods = (PDEBUG_MODULE_ARRAY)
323 buf->ModuleInformation;
324 for (ULONG i = 0; i < mods->Count; ++i)
325 if ((ret = (void *)
326 GetProcAddress ((HMODULE) mods->Modules[i].Base, name)))
327 break;
328 if (!ret)
329 set_errno (ENOENT);
331 RtlDestroyQueryDebugBuffer (buf);
333 else
335 ret = (void *) GetProcAddress ((HMODULE) handle, name);
336 if (!ret)
337 __seterrno ();
339 if (!ret)
340 set_dl_error ("dlsym");
341 debug_printf ("ret %p", ret);
342 return ret;
345 extern "C" int
346 dlclose (void *handle)
348 int ret = 0;
349 if (handle != GetModuleHandle (NULL))
351 /* reference counting */
352 dll *d = dlls.find (handle);
353 if (!d || d->count <= 0)
355 errno = ENOENT;
356 ret = -1;
358 else
360 --d->count;
361 if (!FreeLibrary ((HMODULE) handle))
363 __seterrno ();
364 ret = -1;
368 if (ret)
369 set_dl_error ("dlclose");
370 return ret;
373 extern "C" char *
374 dlerror ()
376 char *res;
377 if (!_my_tls.locals.dl_error)
378 res = NULL;
379 else
381 _my_tls.locals.dl_error = 0;
382 res = _my_tls.locals.dl_buffer;
384 return res;
387 extern "C" int
388 dladdr (const void *addr, Dl_info *info)
390 HMODULE hModule;
391 BOOL ret = GetModuleHandleEx (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
392 (LPCSTR) addr,
393 &hModule);
394 if (!ret)
395 return 0;
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))
406 return 0;
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);
411 if (conv)
412 return 0;
414 /* Always indicate no symbol matching addr could be found. */
415 info->dli_sname = NULL;
416 info->dli_saddr = NULL;
418 return 1;