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