0
|
1 /*
|
|
2
|
|
3 Package: dyncall
|
|
4 Library: dynload
|
|
5 File: dynload/dynload_unix.c
|
|
6 Description:
|
|
7 License:
|
|
8
|
242
|
9 Copyright (c) 2007-2017 Daniel Adler <dadler@uni-goettingen.de>,
|
0
|
10 Tassilo Philipp <tphilipp@potion-studios.com>
|
|
11
|
|
12 Permission to use, copy, modify, and distribute this software for any
|
|
13 purpose with or without fee is hereby granted, provided that the above
|
|
14 copyright notice and this permission notice appear in all copies.
|
|
15
|
|
16 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
17 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
18 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
19 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
20 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
21 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
22 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
23
|
|
24 */
|
|
25
|
|
26
|
|
27 /*
|
|
28
|
|
29 dynload_unix.c
|
|
30
|
|
31 dynload module for .so (unix) and .dylib (mach-o darwin/OS X) files
|
|
32
|
|
33 */
|
|
34
|
|
35
|
|
36 #include "dynload.h"
|
242
|
37 #include "../autovar/autovar_OS.h"
|
0
|
38
|
242
|
39 #include <string.h>
|
|
40
|
|
41 #if defined(__GLIBC__) /* to access dlinfo */
|
|
42 # define _GNU_SOURCE
|
|
43 # define __USE_GNU
|
|
44 #endif
|
0
|
45 #include <dlfcn.h>
|
|
46
|
|
47
|
|
48 DLLib* dlLoadLibrary(const char* libPath)
|
|
49 {
|
232
|
50 return (DLLib*)dlopen(libPath, RTLD_NOW|RTLD_GLOBAL); //@@@ should use RTLD_LAZY, maybe?
|
0
|
51 }
|
|
52
|
|
53
|
242
|
54 void* dlFindSymbol(DLLib* pLib, const char* pSymbolName)
|
0
|
55 {
|
242
|
56 return dlsym((void*)pLib, pSymbolName);
|
|
57 }
|
|
58
|
|
59
|
|
60 void dlFreeLibrary(DLLib* pLib)
|
|
61 {
|
|
62 /* Check for NULL for cross-platform consistency. *BSD seems to do that in
|
|
63 dlclose, Linux does not. POSIX states "if handle does not refer to an open
|
|
64 object, dlclose() returns a non-zero value", which unfortunately sounds
|
|
65 like it's not explicitly specified. */
|
|
66 if(pLib)
|
|
67 dlclose((void*)pLib);
|
0
|
68 }
|
|
69
|
|
70
|
246
|
71 /* prefer RTLD_NOLOAD for code below that merely checks lib names */
|
|
72 #if defined(RTLD_NOLOAD)
|
|
73 # define RTLD_LIGHTEST RTLD_NOLOAD
|
|
74 #else
|
|
75 # define RTLD_LIGHTEST RTLD_LAZY
|
|
76 #endif
|
|
77
|
|
78
|
242
|
79 /* code for dlGetLibraryPath differs on Darwin */
|
|
80 #if defined(OS_Darwin)
|
|
81
|
|
82 #include <stdint.h>
|
|
83 #include <mach-o/dyld.h>
|
|
84
|
|
85 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
|
0
|
86 {
|
242
|
87 uint32_t i;
|
|
88 int l = -1;
|
|
89
|
|
90 /*if(pLib == RTLD_DEFAULT)
|
|
91 return NULL; @@@ return exec's path */
|
0
|
92
|
242
|
93 /* Darwin's code doesn't come with (non-standard) dlinfo(), so use dyld(1) */
|
|
94 /* code. There doesn't seem to be a direct way to query the library path, */
|
|
95 /* so "double-load" temporarily all already loaded images (just increases */
|
|
96 /* ref count) and compare handles until we found ours. Return the name. */
|
|
97 for(i=_dyld_image_count(); i>0;) /* iterate libs from end, more likely ours */
|
|
98 {
|
|
99 const char* libPath = _dyld_get_image_name(--i);
|
246
|
100 void* lib = dlopen(libPath, RTLD_LIGHTEST);
|
242
|
101 if(lib) {
|
246
|
102 dlclose(lib);
|
|
103 /* compare handle pointers' high bits (in low 2 bits some flags might */
|
|
104 /* be stored - should be safe b/c address needs alignment, anywas) */
|
|
105 if(((intptr_t)pLib ^ (intptr_t)lib) < 4) {
|
242
|
106 l = strlen(libPath);
|
|
107 if(l < bufSize) /* l+'\0' <= bufSize */
|
|
108 strcpy(sOut, libPath);
|
|
109 break;
|
|
110 }
|
|
111 }
|
|
112 }
|
|
113
|
|
114 return l+1; /* strlen + '\0' */
|
0
|
115 }
|
|
116
|
248
|
117 #elif defined(OS_OpenBSD) /* doesn't have dlinfo() but dl_iterate_phdr() --> */
|
242
|
118
|
|
119 /* @@@ dl_iterate_phdr() only exists on OpenBSD >= 3.7 */
|
|
120
|
|
121 #include <sys/types.h>
|
|
122 #include <link.h>
|
|
123
|
|
124 typedef struct {
|
|
125 DLLib* pLib;
|
|
126 char* sOut;
|
|
127 int bufSize;
|
|
128 } iter_phdr_data;
|
|
129
|
|
130 static int iter_phdr_cb(struct dl_phdr_info* info, size_t size, void* data)
|
|
131 {
|
|
132 int l = -1;
|
|
133 iter_phdr_data* d = (iter_phdr_data*)data;
|
|
134 /* unable to relate info->dlpi_addr directly to our dlopen handle, let's */
|
|
135 /* do what we do on macOS above, re-dlopen the already loaded lib (just */
|
|
136 /* increases ref count) and compare handles. */
|
246
|
137 void* lib = dlopen(info->dlpi_name, RTLD_LIGHTEST);
|
242
|
138 if(lib) {
|
246
|
139 dlclose(lib);
|
|
140 if(lib == (void*)d->pLib) {
|
242
|
141 l = strlen(info->dlpi_name);
|
|
142 if(l < d->bufSize) /* l+'\0' <= bufSize */
|
|
143 strcpy(d->sOut, info->dlpi_name);
|
|
144 }
|
|
145 }
|
|
146 return l+1; /* strlen + '\0'; is 0 if lib not found, which continues iter */
|
|
147 }
|
|
148
|
|
149 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize)
|
|
150 {
|
|
151 iter_phdr_data d = { pLib, sOut, bufSize };
|
|
152 return dl_iterate_phdr(iter_phdr_cb, &d);
|
|
153 }
|
|
154
|
248
|
155 #elif defined(OS_BeOS) /* neither dlinfo(), nor dl_iterate_phdr(), but ltdl stuff*/
|
|
156
|
|
157 #if 0
|
|
158 #include <ltdl.h>
|
|
159
|
|
160 typedef struct { // @@@share
|
|
161 DLLib* pLib;
|
|
162 char* sOut;
|
|
163 int bufSize;
|
|
164 } iter_phdr_data;
|
|
165
|
|
166 static int handle_map_cb(lt_dlhandle h, void* data)
|
|
167 {
|
|
168 int l = -1;
|
|
169 iter_phdr_data* d = (iter_phdr_data*)data;
|
|
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;
|
|
205 void* s = dlsym((void*)pLib, "_fini");
|
|
206 if(s) {
|
|
207 Dl_info i;
|
|
208 if(dladdr(s, &i) != 0) {
|
|
209 l = strlen(i.dli_fname);
|
|
210 if(l < bufSize) /* l+'\0' <= bufSize */
|
|
211 strcpy(sOut, i.dli_fname);
|
|
212 }
|
|
213 }
|
|
214 return l+1; /* strlen + '\0' */
|
|
215 }
|
|
216
|
242
|
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
|
|
234
|