| |
1 /* |
| |
2 * BinReloc - a library for creating relocatable executables |
| |
3 * Written by: Mike Hearn <mike@theoretic.com> |
| |
4 * Hongli Lai <h.lai@chello.nl> |
| |
5 * http://autopackage.org/ |
| |
6 * |
| |
7 * This source code is public domain. You can relicense this code |
| |
8 * under whatever license you want. |
| |
9 * |
| |
10 * NOTE: if you're using C++ and are getting "undefined reference |
| |
11 * to br_*", try renaming prefix.c to prefix.cpp |
| |
12 */ |
| |
13 |
| |
14 /* WARNING, BEFORE YOU MODIFY PREFIX.C: |
| |
15 * |
| |
16 * If you make changes to any of the functions in prefix.c, you MUST |
| |
17 * change the BR_NAMESPACE macro (in prefix.h). |
| |
18 * This way you can avoid symbol table conflicts with other libraries |
| |
19 * that also happen to use BinReloc. |
| |
20 * |
| |
21 * Example: |
| |
22 * #define BR_NAMESPACE(funcName) foobar_ ## funcName |
| |
23 * --> expands br_locate to foobar_br_locate |
| |
24 */ |
| |
25 |
| |
26 #ifndef _PREFIX_C_ |
| |
27 #define _PREFIX_C_ |
| |
28 |
| |
29 #ifdef HAVE_CONFIG_H |
| |
30 #include "config.h" |
| |
31 #endif |
| |
32 |
| |
33 #ifndef BR_PTHREADS |
| |
34 /* Change 1 to 0 if you don't want pthread support */ |
| |
35 #define BR_PTHREADS 1 |
| |
36 #endif /* BR_PTHREADS */ |
| |
37 |
| |
38 #include <stdlib.h> |
| |
39 #include <stdio.h> |
| |
40 #include <limits.h> |
| |
41 #include <string.h> |
| |
42 #include "prefix.h" |
| |
43 |
| |
44 #ifdef __cplusplus |
| |
45 extern "C" { |
| |
46 #endif /* __cplusplus */ |
| |
47 |
| |
48 |
| |
49 #undef NULL |
| |
50 #define NULL ((void *) 0) |
| |
51 |
| |
52 #ifdef __GNUC__ |
| |
53 #define br_return_val_if_fail(expr,val) if (!(expr)) {fprintf (stderr, "** BinReloc (%s): assertion %s failed\n", __PRETTY_FUNCTION__, #expr); return val;} |
| |
54 #else |
| |
55 #define br_return_val_if_fail(expr,val) if (!(expr)) return val |
| |
56 #endif /* __GNUC__ */ |
| |
57 |
| |
58 |
| |
59 static br_locate_fallback_func fallback_func = NULL; |
| |
60 static void *fallback_data = NULL; |
| |
61 |
| |
62 |
| |
63 #ifdef ENABLE_BINRELOC |
| |
64 #include <sys/types.h> |
| |
65 #include <sys/stat.h> |
| |
66 #include <sys/param.h> |
| |
67 #include <unistd.h> |
| |
68 |
| |
69 |
| |
70 /** |
| |
71 * br_locate: |
| |
72 * symbol: A symbol that belongs to the app/library you want to locate. |
| |
73 * Returns: A newly allocated string containing the full path of the |
| |
74 * app/library that func belongs to, or NULL on error. This |
| |
75 * string should be freed when not when no longer needed. |
| |
76 * |
| |
77 * Finds out to which application or library symbol belongs, then locate |
| |
78 * the full path of that application or library. |
| |
79 * Note that symbol cannot be a pointer to a function. That will not work. |
| |
80 * |
| |
81 * Example: |
| |
82 * --> main.c |
| |
83 * #include "prefix.h" |
| |
84 * #include "libfoo.h" |
| |
85 * |
| |
86 * int main (int argc, char *argv[]) { |
| |
87 * printf ("Full path of this app: %s\n", br_locate (&argc)); |
| |
88 * libfoo_start (); |
| |
89 * return 0; |
| |
90 * } |
| |
91 * |
| |
92 * --> libfoo.c starts here |
| |
93 * #include "prefix.h" |
| |
94 * |
| |
95 * void libfoo_start () { |
| |
96 * --> "" is a symbol that belongs to libfoo (because it's called |
| |
97 * --> from libfoo_start()); that's why this works. |
| |
98 * printf ("libfoo is located in: %s\n", br_locate ("")); |
| |
99 * } |
| |
100 */ |
| |
101 char * |
| |
102 br_locate (void *symbol) |
| |
103 { |
| |
104 char line[5000]; |
| |
105 FILE *f; |
| |
106 char *path; |
| |
107 |
| |
108 br_return_val_if_fail (symbol != NULL, NULL); |
| |
109 |
| |
110 f = fopen ("/proc/self/maps", "r"); |
| |
111 if (!f) { |
| |
112 if (fallback_func) |
| |
113 return fallback_func(symbol, fallback_data); |
| |
114 else |
| |
115 return NULL; |
| |
116 } |
| |
117 |
| |
118 while (!feof (f)) |
| |
119 { |
| |
120 unsigned long start, end; |
| |
121 |
| |
122 if (!fgets (line, sizeof (line), f)) |
| |
123 continue; |
| |
124 if (!strstr (line, " r-xp ") || !strchr (line, '/')) |
| |
125 continue; |
| |
126 |
| |
127 sscanf (line, "%lx-%lx ", &start, &end); |
| |
128 if (symbol >= (void *) start && symbol < (void *) end) |
| |
129 { |
| |
130 char *tmp; |
| |
131 size_t len; |
| |
132 |
| |
133 /* Extract the filename; it is always an absolute path */ |
| |
134 path = strchr (line, '/'); |
| |
135 |
| |
136 /* Get rid of the newline */ |
| |
137 tmp = strrchr (path, '\n'); |
| |
138 if (tmp) *tmp = 0; |
| |
139 |
| |
140 /* Get rid of "(deleted)" */ |
| |
141 len = strlen (path); |
| |
142 if (len > 10 && strcmp (path + len - 10, " (deleted)") == 0) |
| |
143 { |
| |
144 tmp = path + len - 10; |
| |
145 *tmp = 0; |
| |
146 } |
| |
147 |
| |
148 fclose(f); |
| |
149 return strdup (path); |
| |
150 } |
| |
151 } |
| |
152 |
| |
153 fclose (f); |
| |
154 return NULL; |
| |
155 } |
| |
156 |
| |
157 |
| |
158 /** |
| |
159 * br_locate_prefix: |
| |
160 * symbol: A symbol that belongs to the app/library you want to locate. |
| |
161 * Returns: A prefix. This string should be freed when no longer needed. |
| |
162 * |
| |
163 * Locates the full path of the app/library that symbol belongs to, and return |
| |
164 * the prefix of that path, or NULL on error. |
| |
165 * Note that symbol cannot be a pointer to a function. That will not work. |
| |
166 * |
| |
167 * Example: |
| |
168 * --> This application is located in /usr/bin/foo |
| |
169 * br_locate_prefix (&argc); --> returns: "/usr" |
| |
170 */ |
| |
171 char * |
| |
172 br_locate_prefix (void *symbol) |
| |
173 { |
| |
174 char *path, *prefix; |
| |
175 |
| |
176 br_return_val_if_fail (symbol != NULL, NULL); |
| |
177 |
| |
178 path = br_locate (symbol); |
| |
179 if (!path) return NULL; |
| |
180 |
| |
181 prefix = br_extract_prefix (path); |
| |
182 free (path); |
| |
183 return prefix; |
| |
184 } |
| |
185 |
| |
186 |
| |
187 /** |
| |
188 * br_prepend_prefix: |
| |
189 * symbol: A symbol that belongs to the app/library you want to locate. |
| |
190 * path: The path that you want to prepend the prefix to. |
| |
191 * Returns: The new path, or NULL on error. This string should be freed when no |
| |
192 * longer needed. |
| |
193 * |
| |
194 * Gets the prefix of the app/library that symbol belongs to. Prepend that prefix to path. |
| |
195 * Note that symbol cannot be a pointer to a function. That will not work. |
| |
196 * |
| |
197 * Example: |
| |
198 * --> The application is /usr/bin/foo |
| |
199 * br_prepend_prefix (&argc, "/share/foo/data.png"); --> Returns "/usr/share/foo/data.png" |
| |
200 */ |
| |
201 char * |
| |
202 br_prepend_prefix (void *symbol, char *path) |
| |
203 { |
| |
204 char *tmp, *newpath; |
| |
205 |
| |
206 br_return_val_if_fail (symbol != NULL, NULL); |
| |
207 br_return_val_if_fail (path != NULL, NULL); |
| |
208 |
| |
209 tmp = br_locate_prefix (symbol); |
| |
210 if (!tmp) return NULL; |
| |
211 |
| |
212 if (strcmp (tmp, "/") == 0) |
| |
213 newpath = strdup (path); |
| |
214 else |
| |
215 newpath = br_strcat (tmp, path); |
| |
216 |
| |
217 /* Get rid of compiler warning ("br_prepend_prefix never used") */ |
| |
218 if (0) br_prepend_prefix (NULL, NULL); |
| |
219 |
| |
220 free (tmp); |
| |
221 return newpath; |
| |
222 } |
| |
223 |
| |
224 #endif /* ENABLE_BINRELOC */ |
| |
225 |
| |
226 |
| |
227 /* Pthread stuff for thread safetiness */ |
| |
228 #if BR_PTHREADS |
| |
229 |
| |
230 #include <pthread.h> |
| |
231 |
| |
232 static pthread_key_t br_thread_key; |
| |
233 static pthread_once_t br_thread_key_once = PTHREAD_ONCE_INIT; |
| |
234 |
| |
235 |
| |
236 static void |
| |
237 br_thread_local_store_fini () |
| |
238 { |
| |
239 char *specific; |
| |
240 |
| |
241 specific = (char *) pthread_getspecific (br_thread_key); |
| |
242 if (specific) |
| |
243 { |
| |
244 free (specific); |
| |
245 pthread_setspecific (br_thread_key, NULL); |
| |
246 } |
| |
247 pthread_key_delete (br_thread_key); |
| |
248 br_thread_key = 0; |
| |
249 } |
| |
250 |
| |
251 |
| |
252 static void |
| |
253 br_str_free (void *str) |
| |
254 { |
| |
255 if (str) |
| |
256 free (str); |
| |
257 } |
| |
258 |
| |
259 |
| |
260 static void |
| |
261 br_thread_local_store_init () |
| |
262 { |
| |
263 if (pthread_key_create (&br_thread_key, br_str_free) == 0) |
| |
264 atexit (br_thread_local_store_fini); |
| |
265 } |
| |
266 |
| |
267 #else /* BR_PTHREADS */ |
| |
268 |
| |
269 static char *br_last_value = (char *) NULL; |
| |
270 |
| |
271 static void |
| |
272 br_free_last_value () |
| |
273 { |
| |
274 if (br_last_value) |
| |
275 free (br_last_value); |
| |
276 } |
| |
277 |
| |
278 #endif /* BR_PTHREADS */ |
| |
279 |
| |
280 |
| |
281 /** |
| |
282 * br_thread_local_store: |
| |
283 * str: A dynamically allocated string. |
| |
284 * Returns: str. This return value must not be freed. |
| |
285 * |
| |
286 * Store str in a thread-local variable and return str. The next |
| |
287 * you run this function, that variable is freed too. |
| |
288 * This function is created so you don't have to worry about freeing |
| |
289 * strings. Just be careful about doing this sort of thing: |
| |
290 * |
| |
291 * some_function( BR_DATADIR("/one.png"), BR_DATADIR("/two.png") ) |
| |
292 * |
| |
293 * Examples: |
| |
294 * char *foo; |
| |
295 * foo = br_thread_local_store (strdup ("hello")); --> foo == "hello" |
| |
296 * foo = br_thread_local_store (strdup ("world")); --> foo == "world"; "hello" is now freed. |
| |
297 */ |
| |
298 const char * |
| |
299 br_thread_local_store (char *str) |
| |
300 { |
| |
301 #if BR_PTHREADS |
| |
302 char *specific; |
| |
303 |
| |
304 pthread_once (&br_thread_key_once, br_thread_local_store_init); |
| |
305 |
| |
306 specific = (char *) pthread_getspecific (br_thread_key); |
| |
307 br_str_free (specific); |
| |
308 pthread_setspecific (br_thread_key, str); |
| |
309 |
| |
310 #else /* BR_PTHREADS */ |
| |
311 static int initialized = 0; |
| |
312 |
| |
313 if (!initialized) |
| |
314 { |
| |
315 atexit (br_free_last_value); |
| |
316 initialized = 1; |
| |
317 } |
| |
318 |
| |
319 if (br_last_value) |
| |
320 free (br_last_value); |
| |
321 br_last_value = str; |
| |
322 #endif /* BR_PTHREADS */ |
| |
323 |
| |
324 return (const char *) str; |
| |
325 } |
| |
326 |
| |
327 |
| |
328 /** |
| |
329 * br_strcat: |
| |
330 * str1: A string. |
| |
331 * str2: Another string. |
| |
332 * Returns: A newly-allocated string. This string should be freed when no longer needed. |
| |
333 * |
| |
334 * Concatenate str1 and str2 to a newly allocated string. |
| |
335 */ |
| |
336 char * |
| |
337 br_strcat (const char *str1, const char *str2) |
| |
338 { |
| |
339 char *result; |
| |
340 size_t len1, len2; |
| |
341 |
| |
342 if (!str1) str1 = ""; |
| |
343 if (!str2) str2 = ""; |
| |
344 |
| |
345 len1 = strlen (str1); |
| |
346 len2 = strlen (str2); |
| |
347 |
| |
348 result = (char *) malloc (len1 + len2 + 1); |
| |
349 memcpy (result, str1, len1); |
| |
350 memcpy (result + len1, str2, len2); |
| |
351 result[len1 + len2] = '\0'; |
| |
352 |
| |
353 return result; |
| |
354 } |
| |
355 |
| |
356 |
| |
357 /* Emulates glibc's strndup() */ |
| |
358 static char * |
| |
359 br_strndup (char *str, size_t size) |
| |
360 { |
| |
361 char *result = (char *) NULL; |
| |
362 size_t len; |
| |
363 |
| |
364 br_return_val_if_fail (str != (char *) NULL, (char *) NULL); |
| |
365 |
| |
366 len = strlen (str); |
| |
367 if (!len) return strdup (""); |
| |
368 if (size > len) size = len; |
| |
369 |
| |
370 result = (char *) calloc (sizeof (char), len + 1); |
| |
371 memcpy (result, str, size); |
| |
372 return result; |
| |
373 } |
| |
374 |
| |
375 |
| |
376 /** |
| |
377 * br_extract_dir: |
| |
378 * path: A path. |
| |
379 * Returns: A directory name. This string should be freed when no longer needed. |
| |
380 * |
| |
381 * Extracts the directory component of path. Similar to g_dirname() or the dirname |
| |
382 * commandline application. |
| |
383 * |
| |
384 * Example: |
| |
385 * br_extract_dir ("/usr/local/foobar"); --> Returns: "/usr/local" |
| |
386 */ |
| |
387 char * |
| |
388 br_extract_dir (const char *path) |
| |
389 { |
| |
390 char *end, *result; |
| |
391 |
| |
392 br_return_val_if_fail (path != (char *) NULL, (char *) NULL); |
| |
393 |
| |
394 end = strrchr (path, '/'); |
| |
395 if (!end) return strdup ("."); |
| |
396 |
| |
397 while (end > path && *end == '/') |
| |
398 end--; |
| |
399 result = br_strndup ((char *) path, end - path + 1); |
| |
400 if (!*result) |
| |
401 { |
| |
402 free (result); |
| |
403 return strdup ("/"); |
| |
404 } else |
| |
405 return result; |
| |
406 } |
| |
407 |
| |
408 |
| |
409 /** |
| |
410 * br_extract_prefix: |
| |
411 * path: The full path of an executable or library. |
| |
412 * Returns: The prefix, or NULL on error. This string should be freed when no longer needed. |
| |
413 * |
| |
414 * Extracts the prefix from path. This function assumes that your executable |
| |
415 * or library is installed in an LSB-compatible directory structure. |
| |
416 * |
| |
417 * Example: |
| |
418 * br_extract_prefix ("/usr/bin/gnome-panel"); --> Returns "/usr" |
| |
419 * br_extract_prefix ("/usr/local/lib/libfoo.so"); --> Returns "/usr/local" |
| |
420 * br_extract_prefix ("/usr/local/libfoo.so"); --> Returns "/usr" |
| |
421 */ |
| |
422 char * |
| |
423 br_extract_prefix (const char *path) |
| |
424 { |
| |
425 char *end, *tmp, *result; |
| |
426 |
| |
427 br_return_val_if_fail (path != (char *) NULL, (char *) NULL); |
| |
428 |
| |
429 if (!*path) return strdup ("/"); |
| |
430 end = strrchr (path, '/'); |
| |
431 if (!end) return strdup (path); |
| |
432 |
| |
433 tmp = br_strndup ((char *) path, end - path); |
| |
434 if (!*tmp) |
| |
435 { |
| |
436 free (tmp); |
| |
437 return strdup ("/"); |
| |
438 } |
| |
439 end = strrchr (tmp, '/'); |
| |
440 if (!end) return tmp; |
| |
441 |
| |
442 result = br_strndup (tmp, end - tmp); |
| |
443 free (tmp); |
| |
444 |
| |
445 if (!*result) |
| |
446 { |
| |
447 free (result); |
| |
448 result = strdup ("/"); |
| |
449 } |
| |
450 |
| |
451 return result; |
| |
452 } |
| |
453 |
| |
454 |
| |
455 /** |
| |
456 * br_set_fallback_function: |
| |
457 * func: A function to call to find the binary. |
| |
458 * data: User data to pass to func. |
| |
459 * |
| |
460 * Sets a function to call to find the path to the binary, in |
| |
461 * case "/proc/self/maps" can't be opened. The function set should |
| |
462 * return a string that is safe to free with free(). |
| |
463 */ |
| |
464 void |
| |
465 br_set_locate_fallback_func (br_locate_fallback_func func, void *data) |
| |
466 { |
| |
467 fallback_func = func; |
| |
468 fallback_data = data; |
| |
469 } |
| |
470 |
| |
471 |
| |
472 #ifdef __cplusplus |
| |
473 } |
| |
474 #endif /* __cplusplus */ |
| |
475 |
| |
476 #endif /* _PREFIX_C */ |