comparison dynload/dynload_unix.c @ 253:5cfe4322c500

- improved support for older OS versions for dynloads dlGetLibraryPath
author Tassilo Philipp
date Mon, 15 May 2017 02:50:03 +0200
parents ab23f9f2934a
children 9d70178c1ded
comparison
equal deleted inserted replaced
252:047d2829bdf6 253:5cfe4322c500
66 if(pLib) 66 if(pLib)
67 dlclose((void*)pLib); 67 dlclose((void*)pLib);
68 } 68 }
69 69
70 70
71 /* prefer RTLD_NOLOAD for code below that merely checks lib names */ 71
72 /* for dlopen-based dlGetLibraryPath impls below, prefer RTLD_NOLOAD */
73 /* that merely checks lib names */
72 #if defined(RTLD_NOLOAD) 74 #if defined(RTLD_NOLOAD)
73 # define RTLD_LIGHTEST RTLD_NOLOAD 75 # define RTLD_LIGHTEST RTLD_NOLOAD
74 #else 76 #else
75 # define RTLD_LIGHTEST RTLD_LAZY 77 # define RTLD_LIGHTEST RTLD_LAZY
76 #endif 78 #endif
77 79
78 80
79 /* code for dlGetLibraryPath differs on Darwin */ 81 /* code for dlGetLibraryPath is platform specific - if dlinfo() exists use */
80 #if defined(OS_Darwin) 82 /* that (checked through existance of RTLD_DI_LINKMAP, usually a #define */
83 /* for dlinfo(), or by OS (always on Solaris where it's from, usually on */
84 /* Linux, where the flag might be an enum instead, ...) */
85 #if defined(RTLD_DI_LINKMAP) || defined(OS_SunOS) || (defined(OS_Linux) && !defined(DL_DLADDR_TO_LIBPATH))
86
87 #include <link.h>
88
89 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
90 {
91 struct link_map* p;
92 int l = -1;
93 if(dlinfo(pLib, RTLD_DI_LINKMAP, &p) == 0) {
94 l = strlen(p->l_name);
95 if(l < bufSize) /* l+'\0' <= bufSize */
96 strcpy(sOut, p->l_name);
97 }
98 return l+1; /* strlen + '\0' */
99 }
100
101
102 /* specific implementation needed on Darwin -----> */
103 #elif defined(OS_Darwin)
81 104
82 #include <stdint.h> 105 #include <stdint.h>
83 #include <mach-o/dyld.h> 106 #include <mach-o/dyld.h>
84 107
85 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize) 108 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
112 } 135 }
113 136
114 return l+1; /* strlen + '\0' */ 137 return l+1; /* strlen + '\0' */
115 } 138 }
116 139
117 #elif defined(OS_OpenBSD) /* doesn't have dlinfo() but dl_iterate_phdr() --> */ 140
118 141 /* OpenBSD >= 3.7 has dl_iterate_phdr(), use it if not explicitly requesting */
119 /* @@@ dl_iterate_phdr() only exists on OpenBSD >= 3.7 */ 142 /* to use dladdr()-based guessing (set by configure) -----> */
143 #elif defined(OS_OpenBSD) && !defined(DL_DLADDR_TO_LIBPATH)
120 144
121 #include <sys/types.h> 145 #include <sys/types.h>
122 #include <link.h> 146 #include <link.h>
123 147
124 typedef struct { 148 typedef struct {
150 { 174 {
151 iter_phdr_data d = { pLib, sOut, bufSize }; 175 iter_phdr_data d = { pLib, sOut, bufSize };
152 return dl_iterate_phdr(iter_phdr_cb, &d); 176 return dl_iterate_phdr(iter_phdr_cb, &d);
153 } 177 }
154 178
155 #elif defined(OS_BeOS) /* neither dlinfo(), nor dl_iterate_phdr(), but ltdl stuff*/ 179
156 180 /* fallback to dladdr() hack */
157 #if 0 181 #else
158 #include <ltdl.h> 182
159 183 /* if nothing else is available, fall back to guessing using dladdr() - this */
160 typedef struct { // @@@share 184 /* might not always work, as it's trying to getit via the _fini() symbol, */
161 DLLib* pLib; 185 /* which is usually defined in ELF files, but not guaranteed */
162 char* sOut; 186
163 int bufSize; 187 /* @@@Note: On some platforms this might be improved, e.g. on BeOS we have */
164 } iter_phdr_data; 188 /* lt_dlgetinfo, which requires iterating over ltdl stuff, but was unable */
165 189 /* to get that to work (would also introduce a link dependency on libltdl) */
166 static int handle_map_cb(lt_dlhandle h, void* data) 190
167 { 191 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
168 int l = -1; 192 {
169 iter_phdr_data* d = (iter_phdr_data*)data; 193 /* cross fingers that shared object is standard ELF and look for _fini */
170 /* same idea as with dl_iterate_phdr, see above */
171 const lt_dlinfo* dli = lt_dlgetinfo(h);
172 if(dli) {
173 void* lib = dlopen(dli->filename, RTLD_LIGHTEST);
174 if(lib) {
175 dlclose(lib);
176 if(lib == (void*)d->pLib) {
177 l = strlen(dli->filename);
178 if(l < d->bufSize) /* l+'\0' <= bufSize */
179 strcpy(d->sOut, dli->filename);
180 }
181 }
182 }
183 return l+1; /* strlen + '\0'; is 0 if lib not found, which continues iter */
184 }
185
186 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
187 {
188 iter_phdr_data d = { pLib, sOut, bufSize };
189 lt_dlinterface_id ii = lt_dlinterface_register("not_sure_here...", NULL);
190 int l = lt_dlhandle_map(ii, handle_map_cb, &d);
191 lt_dlinterface_free(ii);
192 return l;
193 }
194 #endif
195
196 /* we have lt_dlgetinfo on BeOS, which requires iterating over ltdl stuff, */
197 /* but was unable to get that to work (would also have introduced a link */
198 /* dependency on libltdl); so do a hacky dladdr() based attempt, instead, */
199 /* which might not always work, but probably is ok for nearly all libs */
200
201 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
202 {
203 /* BeOS uses ELF, cross fingers that .so is standard and look for _fini */
204 int l = -1; 194 int l = -1;
205 void* s = dlsym((void*)pLib, "_fini"); 195 void* s = dlsym((void*)pLib, "_fini");
206 if(s) { 196 if(s) {
207 Dl_info i; 197 Dl_info i;
208 if(dladdr(s, &i) != 0) { 198 if(dladdr(s, &i) != 0) {
212 } 202 }
213 } 203 }
214 return l+1; /* strlen + '\0' */ 204 return l+1; /* strlen + '\0' */
215 } 205 }
216 206
217 #else /* use dlinfo() --> */
218
219 #include <link.h>
220
221 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
222 {
223 struct link_map* p;
224 int l = -1;
225 if(dlinfo(pLib, RTLD_DI_LINKMAP, &p) == 0) {
226 l = strlen(p->l_name);
227 if(l < bufSize) /* l+'\0' <= bufSize */
228 strcpy(sOut, p->l_name);
229 }
230 return l+1; /* strlen + '\0' */
231 }
232
233 #endif 207 #endif
234 208