| 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 */ |
|