Mercurial > pub > dyncall > dyncall
comparison dynload/dynload_unix.c @ 315:3840e0188520
- allowing lookup of running executable's path by passing NULL to dynload's dlGetLibraryPath()
author | Tassilo Philipp |
---|---|
date | Wed, 06 Nov 2019 14:13:49 +0100 |
parents | b2e4e23d9953 |
children | 3df50603afa9 |
comparison
equal
deleted
inserted
replaced
314:b2e4e23d9953 | 315:3840e0188520 |
---|---|
37 #include "../autovar/autovar_OS.h" | 37 #include "../autovar/autovar_OS.h" |
38 | 38 |
39 #include <string.h> | 39 #include <string.h> |
40 | 40 |
41 #if defined(__GLIBC__) | 41 #if defined(__GLIBC__) |
42 /* @@@ version check glibc correctly... dl_iterate_phdr(): glibc ver >= 2.2.4*/ | 42 /* @@@ version check glibc more precisely... dl_iterate_phdr(): glibc ver >= 2.2.4*/ |
43 #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 3) | 43 #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 3) |
44 # define DL_USE_GLIBC_ITER_PHDR | 44 # define DL_USE_GLIBC_ITER_PHDR |
45 #endif | 45 #endif |
46 /* to access dl_iterate_phdr(), and related w/ glibc */ | 46 /* to access dl_iterate_phdr(), and related w/ glibc */ |
47 # define _GNU_SOURCE | 47 # define _GNU_SOURCE |
81 #else | 81 #else |
82 # define RTLD_LIGHTEST RTLD_LAZY | 82 # define RTLD_LIGHTEST RTLD_LAZY |
83 #endif | 83 #endif |
84 | 84 |
85 | 85 |
86 /* helper copying string if buffer big enough, returning length (without \0) */ | |
87 static int dl_strlen_strcpy(char* dst, const char* src, int dstSize) | |
88 { | |
89 int l = strlen(src); | |
90 if(l < dstSize) /* l+'\0' <= bufSize */ | |
91 strcpy(dst, src); | |
92 return l; | |
93 } | |
94 | |
86 /* code for dlGetLibraryPath() is platform specific */ | 95 /* code for dlGetLibraryPath() is platform specific */ |
87 | 96 |
88 /* if dlinfo() exists use it (except on glibc, where it exists since version | 97 /* if dlinfo() exists use it (except on glibc, where it exists since version |
89 * 2.3.3, but its implementation is dangerous, as no checks are done whether | 98 * 2.3.3, but its implementation is dangerous, as no checks are done whether |
90 * the handle is valid, thus rendering the returned values useless): check for | 99 * the handle is valid, thus rendering the returned values useless) check for |
91 * RTLD_DI_LINKMAP which is a #define for dlinfo() on most supported targets, | 100 * RTLD_DI_LINKMAP which is a #define for dlinfo() on most supported targets, |
92 * or specifically check the OS (e.g. dlinfo() is originally from Solaris) */ | 101 * or specifically check the OS (e.g. dlinfo() is originally from Solaris) */ |
93 #if (defined(RTLD_DI_LINKMAP) || defined(OS_SunOS)) && !defined(DL_USE_GLIBC_ITER_PHDR) | 102 #if (defined(RTLD_DI_LINKMAP) || defined(OS_SunOS)) && !defined(DL_USE_GLIBC_ITER_PHDR) |
94 | 103 |
95 #include <link.h> | 104 #include <link.h> |
96 | 105 |
97 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize) | 106 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize) |
98 { | 107 { |
99 struct link_map* p = NULL; | 108 struct link_map* p = NULL; |
100 int l = -1; | 109 int l = -1; |
101 if(dlinfo(pLib, RTLD_DI_LINKMAP, &p) == 0 && p) { | 110 if(dlinfo(pLib ? pLib : RTLD_SELF, RTLD_DI_LINKMAP, &p) == 0) |
102 l = strlen(p->l_name); | 111 l = dl_strlen_strcpy(sOut, p->l_name, bufSize); |
103 if(l < bufSize) /* l+'\0' <= bufSize */ | 112 |
104 strcpy(sOut, p->l_name); | |
105 } | |
106 return l+1; /* strlen + '\0' */ | 113 return l+1; /* strlen + '\0' */ |
107 } | 114 } |
108 | 115 |
109 | 116 |
110 /* specific implementation needed on Darwin -----> */ | 117 /* specific implementation needed on Darwin -----> */ |
116 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize) | 123 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize) |
117 { | 124 { |
118 uint32_t i; | 125 uint32_t i; |
119 int l = -1; | 126 int l = -1; |
120 | 127 |
121 /*if(pLib == RTLD_DEFAULT) | 128 /* request info about own process? lookup first loaded image */ |
122 return NULL; @@@ return exec's path */ | 129 if(pLib == NULL) { |
123 | 130 const char* libPath = _dyld_get_image_name(0); //@@@ consider using _NSGetExecutablePath() |
124 /* Darwin's code doesn't come with (non-standard) dlinfo(), so use dyld(1) */ | 131 if(libPath) |
125 /* code. There doesn't seem to be a direct way to query the library path, */ | 132 l = dl_strlen_strcpy(sOut, libPath, bufSize); |
126 /* so "double-load" temporarily all already loaded images (just increases */ | 133 } |
127 /* ref count) and compare handles until we found ours. Return the name. */ | 134 else { |
128 for(i=_dyld_image_count(); i>0;) /* backwards, ours is more likely at end */ | 135 /* Darwin's code doesn't come with (non-standard) dlinfo(), so use dyld(1) |
129 { | 136 * code. There doesn't seem to be a direct way to query the library path, |
130 const char* libPath = _dyld_get_image_name(--i); | 137 * so "double-load" temporarily all already loaded images (just increases |
131 void* lib = dlopen(libPath, RTLD_LIGHTEST); | 138 * ref count) and compare handles until we found ours. Return the name. */ |
132 if(lib) { | 139 for(i=_dyld_image_count(); i>0;) /* backwards, ours is more likely at end */ |
133 dlclose(lib); | 140 { |
134 /* compare handle pointers' high bits (in low 2 bits some flags might */ | 141 const char* libPath = _dyld_get_image_name(--i); |
135 /* be stored - should be safe b/c address needs alignment, anywas) */ | 142 void* lib = dlopen(libPath, RTLD_LIGHTEST); |
136 if(((intptr_t)pLib ^ (intptr_t)lib) < 4) { | 143 if(lib) { |
137 l = strlen(libPath); | 144 dlclose(lib); |
138 if(l < bufSize) /* l+'\0' <= bufSize */ | 145 |
139 strcpy(sOut, libPath); | 146 /* compare handle pointers' high bits (in low 2 bits some flags might */ |
140 break; | 147 /* be stored - should be safe b/c address needs alignment, anyways) */ |
148 if(((intptr_t)pLib ^ (intptr_t)lib) < 4) { | |
149 l = dl_strlen_strcpy(sOut, libPath, bufSize); | |
150 break; | |
151 } | |
141 } | 152 } |
142 } | 153 } |
143 } | 154 } |
144 | 155 |
145 return l+1; /* strlen + '\0' */ | 156 return l+1; /* strlen + '\0' */ |
162 | 173 |
163 static int iter_phdr_cb(struct dl_phdr_info* info, size_t size, void* data) | 174 static int iter_phdr_cb(struct dl_phdr_info* info, size_t size, void* data) |
164 { | 175 { |
165 int l = -1; | 176 int l = -1; |
166 iter_phdr_data* d = (iter_phdr_data*)data; | 177 iter_phdr_data* d = (iter_phdr_data*)data; |
167 /* unable to relate info->dlpi_addr directly to our dlopen handle, let's */ | 178 void* lib = NULL; |
168 /* do what we do on macOS above, re-dlopen the already loaded lib (just */ | 179 |
169 /* increases ref count) and compare handles. */ | 180 /* get loaded object's handle if not requesting info about process itself */ |
170 void* lib = dlopen(info->dlpi_name, RTLD_LIGHTEST); | 181 if(d->pLib != NULL) { |
171 if(lib) { | 182 /* unable to relate info->dlpi_addr directly to our dlopen handle, let's |
172 dlclose(lib); | 183 * do what we do on macOS above, re-dlopen the already loaded lib (just |
173 if(lib == (void*)d->pLib) { | 184 * increases ref count) and compare handles */ |
174 l = strlen(info->dlpi_name); | 185 lib = dlopen(info->dlpi_name, RTLD_LIGHTEST); |
175 if(l < d->bufSize) /* l+'\0' <= bufSize */ | 186 if(lib) |
176 strcpy(d->sOut, info->dlpi_name); | 187 dlclose(lib); |
188 } | |
189 | |
190 /* compare handles and get name if found; if d->pLib == NULL this will | |
191 enter info on first iterated object, which is the process itself */ | |
192 if(lib == (void*)d->pLib) { | |
193 l = dl_strlen_strcpy(d->sOut, info->dlpi_name, d->bufSize); | |
194 | |
195 /* on some platforms (e.g. Linux) dlpi_name is empty for the main process' | |
196 object, but dlpi_addr is given, in that case lookup name via dladdr */ | |
197 if(l == 0 && d->pLib == NULL && (void*)info->dlpi_addr != NULL) { | |
198 Dl_info i; | |
199 if(dladdr((void*)info->dlpi_addr, &i) != 0) | |
200 l = dl_strlen_strcpy(d->sOut, i.dli_fname, d->bufSize); | |
177 } | 201 } |
178 } | 202 } |
203 | |
179 return l+1; /* strlen + '\0'; is 0 if lib not found, which continues iter */ | 204 return l+1; /* strlen + '\0'; is 0 if lib not found, which continues iter */ |
180 } | 205 } |
181 | 206 |
182 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize) | 207 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize) |
183 { | 208 { |
204 /* lt_dlgetinfo, which requires iterating over ltdl stuff, but was unable */ | 229 /* lt_dlgetinfo, which requires iterating over ltdl stuff, but was unable */ |
205 /* to get that to work (would also introduce a link dependency on libltdl) */ | 230 /* to get that to work (would also introduce a link dependency on libltdl) */ |
206 | 231 |
207 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize) | 232 int dlGetLibraryPath(DLLib* pLib, char* sOut, int bufSize) |
208 { | 233 { |
234 /*@@@ missing handler for pLib == NULL*/ | |
209 /* cross fingers that shared object is standard ELF and look for _fini */ | 235 /* cross fingers that shared object is standard ELF and look for _fini */ |
210 int l = -1; | 236 int l = -1; |
211 void* s = dlsym((void*)pLib, "_fini"); | 237 void* s = dlsym((void*)pLib, "_fini"); |
212 if(s) { | 238 if(s) { |
213 Dl_info i; | 239 Dl_info i; |
214 if(dladdr(s, &i) != 0) { | 240 if(dladdr(s, &i) != 0) |
215 l = strlen(i.dli_fname); | 241 l = dl_strlen_strcpy(sOut, i.dli_fname, bufSize); |
216 if(l < bufSize) /* l+'\0' <= bufSize */ | |
217 strcpy(sOut, i.dli_fname); | |
218 } | |
219 } | 242 } |
220 return l+1; /* strlen + '\0' */ | 243 return l+1; /* strlen + '\0' */ |
221 } | 244 } |
222 | 245 |
223 #endif | 246 #endif |