]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* dlfcn.cc |
2 | ||
bc837d22 CF |
3 | Copyright 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, |
4 | 2010, 2011 Red Hat, Inc. | |
1fd5e000 CF |
5 | |
6 | This file is part of Cygwin. | |
7 | ||
8 | This software is a copyrighted work licensed under the terms of the | |
9 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
10 | details. */ | |
11 | ||
4c8d72de | 12 | #include "winsup.h" |
599b41c4 | 13 | #include <psapi.h> |
1fd5e000 | 14 | #include <stdlib.h> |
1fd5e000 | 15 | #include <ctype.h> |
7ac61736 | 16 | #include "path.h" |
95a8465b | 17 | #include "perprocess.h" |
f0338f54 | 18 | #include "dlfcn.h" |
29d52c8a | 19 | #include "cygtls.h" |
0c4cb560 | 20 | #include "tls_pbuf.h" |
31ddf45d | 21 | #include "ntdll.h" |
1fd5e000 CF |
22 | |
23 | static void __stdcall | |
24 | set_dl_error (const char *str) | |
25 | { | |
ac5e21b0 | 26 | strcpy (_my_tls.locals.dl_buffer, strerror (get_errno ())); |
29d52c8a | 27 | _my_tls.locals.dl_error = 1; |
1fd5e000 CF |
28 | } |
29 | ||
01e6597b CF |
30 | /* Look for an executable file given the name and the environment |
31 | variable to use for searching (eg., PATH); returns the full | |
32 | pathname (static buffer) if found or NULL if not. */ | |
aa73152e CF |
33 | inline const char * __stdcall |
34 | check_path_access (const char *mywinenv, const char *name, path_conv& buf) | |
1fd5e000 | 35 | { |
4b84e3dc | 36 | return find_exec (name, buf, mywinenv, FE_NNF | FE_NATIVE | FE_CWD | FE_DLL); |
1fd5e000 CF |
37 | } |
38 | ||
e2d88014 CV |
39 | /* Search LD_LIBRARY_PATH for dll, if it exists. Search /usr/bin and /usr/lib |
40 | by default. Return valid full path in path_conv real_filename. */ | |
41 | static inline bool | |
42 | gfpod_helper (const char *name, path_conv &real_filename) | |
43 | { | |
44 | if (isabspath (name)) | |
45 | real_filename.check (name, PC_SYM_FOLLOW | PC_NULLEMPTY); | |
46 | else if (!check_path_access ("LD_LIBRARY_PATH=", name, real_filename)) | |
47 | check_path_access ("/usr/bin:/usr/lib", name, real_filename); | |
48 | if (!real_filename.exists ()) | |
49 | real_filename.error = ENOENT; | |
50 | return !real_filename.error; | |
51 | } | |
52 | ||
54155bc3 CV |
53 | static bool __stdcall |
54 | get_full_path_of_dll (const char* str, path_conv &real_filename) | |
1fd5e000 | 55 | { |
aa73152e | 56 | int len = strlen (str); |
1fd5e000 | 57 | |
54155bc3 CV |
58 | /* empty string? */ |
59 | if (len == 0) | |
60 | { | |
61 | set_errno (EINVAL); | |
62 | return false; /* Yes. Let caller deal with it. */ | |
63 | } | |
1fd5e000 | 64 | |
54155bc3 CV |
65 | tmp_pathbuf tp; |
66 | char *name = tp.c_get (); | |
1fd5e000 | 67 | |
aa73152e | 68 | strcpy (name, str); /* Put it somewhere where we can manipulate it. */ |
1fd5e000 | 69 | |
e2d88014 CV |
70 | char *basename = strrchr (name, '/'); |
71 | basename = basename ? basename + 1 : name; | |
72 | char *suffix = strrchr (name, '.'); | |
73 | if (suffix && suffix < basename) | |
74 | suffix = NULL; | |
75 | ||
76 | /* Is suffix ".so"? */ | |
77 | if (suffix && !strcmp (suffix, ".so")) | |
78 | { | |
79 | /* Does the file exist? */ | |
80 | if (gfpod_helper (name, real_filename)) | |
81 | return true; | |
82 | /* No, replace ".so" with ".dll". */ | |
83 | strcpy (suffix, ".dll"); | |
84 | } | |
85 | /* Does the filename start with "lib"? */ | |
86 | if (!strncmp (basename, "lib", 3)) | |
87 | { | |
88 | /* Yes, replace "lib" with "cyg". */ | |
89 | strncpy (basename, "cyg", 3); | |
90 | /* Does the file exist? */ | |
91 | if (gfpod_helper (name, real_filename)) | |
92 | return true; | |
93 | /* No, revert back to "lib". */ | |
94 | strncpy (basename, "lib", 3); | |
95 | } | |
96 | if (gfpod_helper (name, real_filename)) | |
97 | return true; | |
1fd5e000 | 98 | |
e2d88014 CV |
99 | /* If nothing worked, create a relative path from the original incoming |
100 | filename and let LoadLibrary search for it using the system default | |
101 | DLL search path. */ | |
102 | real_filename.check (str, PC_SYM_FOLLOW | PC_NOFULL | PC_NULLEMPTY); | |
aec297d5 | 103 | if (!real_filename.error) |
54155bc3 | 104 | return true; |
1fd5e000 | 105 | |
aec297d5 | 106 | set_errno (real_filename.error); |
54155bc3 | 107 | return false; |
1fd5e000 CF |
108 | } |
109 | ||
6bc64eac CV |
110 | extern "C" void * |
111 | dlopen (const char *name, int flags) | |
1fd5e000 | 112 | { |
6bc64eac | 113 | void *ret = NULL; |
1fd5e000 | 114 | |
aa73152e | 115 | if (name == NULL) |
31ddf45d CV |
116 | { |
117 | ret = (void *) GetModuleHandle (NULL); /* handle for the current module */ | |
118 | if (!ret) | |
b86f999a | 119 | __seterrno (); |
31ddf45d | 120 | } |
1fd5e000 CF |
121 | else |
122 | { | |
01e6597b | 123 | /* handle for the named library */ |
54155bc3 | 124 | path_conv pc; |
6bc64eac | 125 | if (get_full_path_of_dll (name, pc)) |
aa73152e | 126 | { |
54155bc3 CV |
127 | tmp_pathbuf tp; |
128 | wchar_t *path = tp.w_get (); | |
129 | ||
130 | pc.get_wide_win32_path (path); | |
df958670 | 131 | /* Check if the last path component contains a dot. If so, |
6bc64eac | 132 | leave the filename alone. Otherwise add a trailing dot |
df958670 CV |
133 | to override LoadLibrary's automatic adding of a ".dll" suffix. */ |
134 | wchar_t *last_bs = wcsrchr (path, L'\\'); | |
135 | if (last_bs && !wcschr (last_bs, L'.')) | |
136 | wcscat (last_bs, L"."); | |
ce5eb135 CV |
137 | |
138 | /* Workaround for broken DLLs built against Cygwin versions 1.7.0-49 | |
139 | up to 1.7.0-57. They override the cxx_malloc pointer in their | |
140 | DLL initialization code even if loaded dynamically. This is a | |
141 | no-no since a later dlclose lets cxx_malloc point into nirvana. | |
142 | The below kludge "fixes" that by reverting the original cxx_malloc | |
143 | pointer after LoadLibrary. This implies that their overrides | |
144 | won't be applied; that's OK. All overrides should be present at | |
145 | final link time, as Windows doesn't allow undefined references; | |
146 | it would actually be wrong for a dlopen'd DLL to opportunistically | |
147 | override functions in a way that wasn't known then. We're not | |
148 | going to try and reproduce the full ELF dynamic loader here! */ | |
149 | ||
150 | /* Store original cxx_malloc pointer. */ | |
151 | struct per_process_cxx_malloc *tmp_malloc; | |
152 | tmp_malloc = __cygwin_user_data.cxx_malloc; | |
153 | ||
6bc64eac CV |
154 | if (!(flags & RTLD_NOLOAD) |
155 | || (ret = GetModuleHandleW (path)) != NULL) | |
156 | { | |
157 | ret = (void *) LoadLibraryW (path); | |
158 | if (ret && (flags & RTLD_NODELETE) | |
159 | && !GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_PIN, path, | |
160 | (HMODULE *) &ret)) | |
161 | { | |
162 | /* Windows 2000 is missing the GetModuleHandleEx call, so we | |
833db548 CV |
163 | use a non-documented way to set the DLL to "don't free". |
164 | This is how it works: Fetch the Windows Loader data from | |
165 | the PEB. Iterate backwards through the list of loaded | |
166 | DLLs and compare the DllBase address with the address | |
167 | returned by LoadLibrary. If they are equal we found the | |
168 | right entry. Now set the LoadCount to -1, which is the | |
169 | marker for a DLL which should never be free'd. */ | |
170 | PPEB_LDR_DATA ldr = NtCurrentTeb ()->Peb->Ldr; | |
171 | ||
172 | for (PLDR_DATA_TABLE_ENTRY entry = (PLDR_DATA_TABLE_ENTRY) | |
173 | ldr->InLoadOrderModuleList.Blink; | |
174 | entry && entry->DllBase; | |
175 | entry = (PLDR_DATA_TABLE_ENTRY) | |
176 | entry->InLoadOrderLinks.Blink) | |
177 | { | |
178 | if (entry->DllBase == ret) | |
179 | { | |
180 | entry->LoadCount = (WORD) -1; | |
181 | break; | |
182 | } | |
183 | } | |
6bc64eac CV |
184 | } |
185 | } | |
ce5eb135 CV |
186 | |
187 | /* Restore original cxx_malloc pointer. */ | |
188 | __cygwin_user_data.cxx_malloc = tmp_malloc; | |
189 | ||
31ddf45d | 190 | if (!ret) |
aa73152e CF |
191 | __seterrno (); |
192 | } | |
1fd5e000 CF |
193 | } |
194 | ||
195 | if (!ret) | |
196 | set_dl_error ("dlopen"); | |
197 | debug_printf ("ret %p", ret); | |
198 | ||
1fd5e000 CF |
199 | return ret; |
200 | } | |
201 | ||
6bc64eac | 202 | extern "C" void * |
1fd5e000 CF |
203 | dlsym (void *handle, const char *name) |
204 | { | |
599b41c4 | 205 | void *ret = NULL; |
31ddf45d | 206 | |
599b41c4 CV |
207 | if (handle == RTLD_DEFAULT) |
208 | { /* search all modules */ | |
31ddf45d CV |
209 | PDEBUG_BUFFER buf; |
210 | NTSTATUS status; | |
211 | ||
212 | buf = RtlCreateQueryDebugBuffer (0, FALSE); | |
213 | if (!buf) | |
05726ddd | 214 | { |
31ddf45d | 215 | set_errno (ENOMEM); |
05726ddd CF |
216 | set_dl_error ("dlsym"); |
217 | return NULL; | |
218 | } | |
31ddf45d CV |
219 | status = RtlQueryProcessDebugInformation (GetCurrentProcessId (), |
220 | PDI_MODULES, buf); | |
221 | if (!NT_SUCCESS (status)) | |
222 | __seterrno_from_nt_status (status); | |
223 | else | |
224 | { | |
225 | PDEBUG_MODULE_ARRAY mods = (PDEBUG_MODULE_ARRAY) | |
226 | buf->ModuleInformation; | |
227 | for (ULONG i = 0; i < mods->Count; ++i) | |
228 | if ((ret = (void *) | |
229 | GetProcAddress ((HMODULE) mods->Modules[i].Base, name))) | |
230 | break; | |
231 | if (!ret) | |
232 | set_errno (ENOENT); | |
233 | } | |
234 | RtlDestroyQueryDebugBuffer (buf); | |
599b41c4 CV |
235 | } |
236 | else | |
31ddf45d CV |
237 | { |
238 | ret = (void *) GetProcAddress ((HMODULE) handle, name); | |
239 | if (!ret) | |
240 | __seterrno (); | |
241 | } | |
1fd5e000 CF |
242 | if (!ret) |
243 | set_dl_error ("dlsym"); | |
244 | debug_printf ("ret %p", ret); | |
245 | return ret; | |
246 | } | |
247 | ||
6bc64eac | 248 | extern "C" int |
1fd5e000 CF |
249 | dlclose (void *handle) |
250 | { | |
d5d5bf4d CF |
251 | int ret; |
252 | if (handle == GetModuleHandle (NULL)) | |
1fd5e000 | 253 | ret = 0; |
98a97ac6 CF |
254 | else if (FreeLibrary ((HMODULE) handle)) |
255 | ret = 0; | |
d5d5bf4d | 256 | else |
98a97ac6 | 257 | ret = -1; |
1fd5e000 CF |
258 | if (ret) |
259 | set_dl_error ("dlclose"); | |
1fd5e000 CF |
260 | return ret; |
261 | } | |
262 | ||
6bc64eac | 263 | extern "C" char * |
1fd5e000 CF |
264 | dlerror () |
265 | { | |
a5a93a62 | 266 | char *res; |
29d52c8a | 267 | if (!_my_tls.locals.dl_error) |
a5a93a62 CF |
268 | res = NULL; |
269 | else | |
270 | { | |
29d52c8a CF |
271 | _my_tls.locals.dl_error = 0; |
272 | res = _my_tls.locals.dl_buffer; | |
a5a93a62 CF |
273 | } |
274 | return res; | |
1fd5e000 | 275 | } |