2 * BinReloc - a library for creating relocatable executables
3 * Written by: Hongli Lai <h.lai@chello.nl>
4 * http://autopackage.org/
6 * This source code is public domain. You can relicense this code
7 * under whatever license you want.
9 * See http://autopackage.org/docs/binreloc/ for
10 * more information and how to use this.
13 #ifndef __BINRELOC_C__
14 #define __BINRELOC_C__
16 #ifdef ENABLE_BINRELOC
17 #include <sys/types.h>
20 #endif /* ENABLE_BINRELOC */
29 #endif /* __cplusplus */
34 * Find the canonical filename of the executable. Returns the filename
35 * (which must be freed) or NULL on error. If the parameter 'error' is
36 * not NULL, the error code will be stored there, if an error occured.
39 _br_find_exe (BrInitError *error)
41 #ifndef ENABLE_BINRELOC
43 *error = BR_INIT_ERROR_DISABLED;
46 char *path, *path2, *line, *result;
52 /* Read from /proc/self/exe (symlink) */
53 if (sizeof (path) > SSIZE_MAX)
54 buf_size = SSIZE_MAX - 1;
56 buf_size = PATH_MAX - 1;
57 path = (char *) malloc (buf_size);
59 /* Cannot allocate memory. */
61 *error = BR_INIT_ERROR_NOMEM;
64 path2 = (char *) malloc (buf_size);
66 /* Cannot allocate memory. */
68 *error = BR_INIT_ERROR_NOMEM;
73 strncpy (path2, "/proc/self/exe", buf_size - 1);
78 size = readlink (path2, path, buf_size - 1);
85 /* readlink() success. */
88 /* Check whether the symlink's target is also a symlink.
89 * We want to get the final target. */
90 i = stat (path, &stat_buf);
98 if (!S_ISLNK (stat_buf.st_mode)) {
99 /* path is not a symlink. Done. */
104 /* path is a symlink. Continue loop and resolve this. */
105 strncpy (path, path2, buf_size - 1);
109 /* readlink() or stat() failed; this can happen when the program is
110 * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
112 buf_size = PATH_MAX + 128;
113 line = (char *) realloc (path, buf_size);
115 /* Cannot allocate memory. */
118 *error = BR_INIT_ERROR_NOMEM;
122 f = fopen ("/proc/self/maps", "r");
126 *error = BR_INIT_ERROR_OPEN_MAPS;
130 /* The first entry should be the executable name. */
131 result = fgets (line, (int) buf_size, f);
132 if (result == NULL) {
136 *error = BR_INIT_ERROR_READ_MAPS;
140 /* Get rid of newline character. */
141 buf_size = strlen (line);
143 /* Huh? An empty string? */
147 *error = BR_INIT_ERROR_INVALID_MAPS;
150 if (line[buf_size - 1] == 10)
151 line[buf_size - 1] = 0;
153 /* Extract the filename; it is always an absolute path. */
154 path = strchr (line, '/');
157 if (strstr (line, " r-xp ") == NULL || path == NULL) {
161 *error = BR_INIT_ERROR_INVALID_MAPS;
165 path = strdup (path);
169 #endif /* ENABLE_BINRELOC */
174 * Find the canonical filename of the executable which owns symbol.
175 * Returns a filename which must be freed, or NULL on error.
178 _br_find_exe_for_symbol (const void *symbol, BrInitError *error)
180 #ifndef ENABLE_BINRELOC
182 *error = BR_INIT_ERROR_DISABLED;
183 return (char *) NULL;
185 #define SIZE PATH_MAX + 100
187 size_t address_string_len;
188 char *address_string, line[SIZE], *found;
191 return (char *) NULL;
193 f = fopen ("/proc/self/maps", "r");
195 return (char *) NULL;
197 address_string_len = 4;
198 address_string = (char *) malloc (address_string_len);
199 found = (char *) NULL;
202 char *start_addr, *end_addr, *end_addr_end, *file;
203 void *start_addr_p, *end_addr_p;
206 if (fgets (line, SIZE, f) == NULL)
210 if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
215 end_addr = strchr (line, '-');
216 file = strchr (line, '/');
218 /* More sanity check. */
219 if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
224 end_addr_end = strchr (end_addr, ' ');
225 if (end_addr_end == NULL)
228 end_addr_end[0] = '\0';
232 if (file[len - 1] == '\n')
233 file[len - 1] = '\0';
235 /* Get rid of "(deleted)" from the filename. */
237 if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
238 file[len - 10] = '\0';
240 /* I don't know whether this can happen but better safe than sorry. */
241 len = strlen (start_addr);
242 if (len != strlen (end_addr))
246 /* Transform the addresses into a string in the form of 0xdeadbeef,
247 * then transform that into a pointer. */
248 if (address_string_len < len + 3) {
249 address_string_len = len + 3;
250 address_string = (char *) realloc (address_string, address_string_len);
253 memcpy (address_string, "0x", 2);
254 memcpy (address_string + 2, start_addr, len);
255 address_string[2 + len] = '\0';
256 sscanf (address_string, "%p", &start_addr_p);
258 memcpy (address_string, "0x", 2);
259 memcpy (address_string + 2, end_addr, len);
260 address_string[2 + len] = '\0';
261 sscanf (address_string, "%p", &end_addr_p);
264 if (symbol >= start_addr_p && symbol < end_addr_p) {
270 free (address_string);
274 return (char *) NULL;
276 return strdup (found);
277 #endif /* ENABLE_BINRELOC */
281 #ifndef BINRELOC_RUNNING_DOXYGEN
283 #define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */
286 static char *exe = (char *) NULL;
289 /** Initialize the BinReloc library (for applications).
291 * This function must be called before using any other BinReloc functions.
292 * It attempts to locate the application's canonical filename.
294 * @note If you want to use BinReloc for a library, then you should call
295 * br_init_lib() instead.
297 * @param error If BinReloc failed to initialize, then the error code will
298 * be stored in this variable. Set to NULL if you want to
299 * ignore this. See #BrInitError for a list of error codes.
301 * @returns 1 on success, 0 if BinReloc failed to initialize.
304 br_init (BrInitError *error)
306 exe = _br_find_exe (error);
311 /** Initialize the BinReloc library (for libraries).
313 * This function must be called before using any other BinReloc functions.
314 * It attempts to locate the calling library's canonical filename.
316 * @note The BinReloc source code MUST be included in your library, or this
317 * function won't work correctly.
319 * @param error If BinReloc failed to initialize, then the error code will
320 * be stored in this variable. Set to NULL if you want to
321 * ignore this. See #BrInitError for a list of error codes.
323 * @returns 1 on success, 0 if a filename cannot be found.
326 br_init_lib (BrInitError *error)
328 exe = _br_find_exe_for_symbol ((const void *) "", error);
333 /** Find the canonical filename of the current application.
335 * @param default_exe A default filename which will be used as fallback.
336 * @returns A string containing the application's canonical filename,
337 * which must be freed when no longer necessary. If BinReloc is
338 * not initialized, or if br_init() failed, then a copy of
339 * default_exe will be returned. If default_exe is NULL, then
340 * NULL will be returned.
343 br_find_exe (const char *default_exe)
345 if (exe == (char *) NULL) {
346 /* BinReloc is not initialized. */
347 if (default_exe != (const char *) NULL)
348 return strdup (default_exe);
350 return (char *) NULL;
356 /** Locate the directory in which the current application is installed.
358 * The prefix is generated by the following pseudo-code evaluation:
363 * @param default_dir A default directory which will used as fallback.
364 * @return A string containing the directory, which must be freed when no
365 * longer necessary. If BinReloc is not initialized, or if the
366 * initialization function failed, then a copy of default_dir
367 * will be returned. If default_dir is NULL, then NULL will be
371 br_find_exe_dir (const char *default_dir)
374 /* BinReloc not initialized. */
375 if (default_dir != NULL)
376 return strdup (default_dir);
381 return br_dirname (exe);
385 /** Locate the prefix in which the current application is installed.
387 * The prefix is generated by the following pseudo-code evaluation:
389 * dirname(dirname(exename))
392 * @param default_prefix A default prefix which will used as fallback.
393 * @return A string containing the prefix, which must be freed when no
394 * longer necessary. If BinReloc is not initialized, or if
395 * the initialization function failed, then a copy of default_prefix
396 * will be returned. If default_prefix is NULL, then NULL will be returned.
399 br_find_prefix (const char *default_prefix)
403 if (exe == (char *) NULL) {
404 /* BinReloc not initialized. */
405 if (default_prefix != (const char *) NULL)
406 return strdup (default_prefix);
408 return (char *) NULL;
411 dir1 = br_dirname (exe);
412 dir2 = br_dirname (dir1);
418 /** Locate the application's binary folder.
420 * The path is generated by the following pseudo-code evaluation:
425 * @param default_bin_dir A default path which will used as fallback.
426 * @return A string containing the bin folder's path, which must be freed when
427 * no longer necessary. If BinReloc is not initialized, or if
428 * the initialization function failed, then a copy of default_bin_dir will
429 * be returned. If default_bin_dir is NULL, then NULL will be returned.
432 br_find_bin_dir (const char *default_bin_dir)
436 prefix = br_find_prefix ((const char *) NULL);
437 if (prefix == (char *) NULL) {
438 /* BinReloc not initialized. */
439 if (default_bin_dir != (const char *) NULL)
440 return strdup (default_bin_dir);
442 return (char *) NULL;
445 dir = br_build_path (prefix, "bin");
451 /** Locate the application's superuser binary folder.
453 * The path is generated by the following pseudo-code evaluation:
458 * @param default_sbin_dir A default path which will used as fallback.
459 * @return A string containing the sbin folder's path, which must be freed when
460 * no longer necessary. If BinReloc is not initialized, or if the
461 * initialization function failed, then a copy of default_sbin_dir will
462 * be returned. If default_bin_dir is NULL, then NULL will be returned.
465 br_find_sbin_dir (const char *default_sbin_dir)
469 prefix = br_find_prefix ((const char *) NULL);
470 if (prefix == (char *) NULL) {
471 /* BinReloc not initialized. */
472 if (default_sbin_dir != (const char *) NULL)
473 return strdup (default_sbin_dir);
475 return (char *) NULL;
478 dir = br_build_path (prefix, "sbin");
484 /** Locate the application's data folder.
486 * The path is generated by the following pseudo-code evaluation:
491 * @param default_data_dir A default path which will used as fallback.
492 * @return A string containing the data folder's path, which must be freed when
493 * no longer necessary. If BinReloc is not initialized, or if the
494 * initialization function failed, then a copy of default_data_dir
495 * will be returned. If default_data_dir is NULL, then NULL will be
499 br_find_data_dir (const char *default_data_dir)
503 prefix = br_find_prefix ((const char *) NULL);
504 if (prefix == (char *) NULL) {
505 /* BinReloc not initialized. */
506 if (default_data_dir != (const char *) NULL)
507 return strdup (default_data_dir);
509 return (char *) NULL;
512 dir = br_build_path (prefix, "share");
518 /** Locate the application's localization folder.
520 * The path is generated by the following pseudo-code evaluation:
522 * prefix + "/share/locale"
525 * @param default_locale_dir A default path which will used as fallback.
526 * @return A string containing the localization folder's path, which must be freed when
527 * no longer necessary. If BinReloc is not initialized, or if the
528 * initialization function failed, then a copy of default_locale_dir will be returned.
529 * If default_locale_dir is NULL, then NULL will be returned.
532 br_find_locale_dir (const char *default_locale_dir)
534 char *data_dir, *dir;
536 data_dir = br_find_data_dir ((const char *) NULL);
537 if (data_dir == (char *) NULL) {
538 /* BinReloc not initialized. */
539 if (default_locale_dir != (const char *) NULL)
540 return strdup (default_locale_dir);
542 return (char *) NULL;
545 dir = br_build_path (data_dir, "locale");
551 /** Locate the application's library folder.
553 * The path is generated by the following pseudo-code evaluation:
558 * @param default_lib_dir A default path which will used as fallback.
559 * @return A string containing the library folder's path, which must be freed when
560 * no longer necessary. If BinReloc is not initialized, or if the initialization
561 * function failed, then a copy of default_lib_dir will be returned.
562 * If default_lib_dir is NULL, then NULL will be returned.
565 br_find_lib_dir (const char *default_lib_dir)
569 prefix = br_find_prefix ((const char *) NULL);
570 if (prefix == (char *) NULL) {
571 /* BinReloc not initialized. */
572 if (default_lib_dir != (const char *) NULL)
573 return strdup (default_lib_dir);
575 return (char *) NULL;
578 dir = br_build_path (prefix, "lib");
584 /** Locate the application's libexec folder.
586 * The path is generated by the following pseudo-code evaluation:
588 * prefix + "/libexec"
591 * @param default_libexec_dir A default path which will used as fallback.
592 * @return A string containing the libexec folder's path, which must be freed when
593 * no longer necessary. If BinReloc is not initialized, or if the initialization
594 * function failed, then a copy of default_libexec_dir will be returned.
595 * If default_libexec_dir is NULL, then NULL will be returned.
598 br_find_libexec_dir (const char *default_libexec_dir)
602 prefix = br_find_prefix ((const char *) NULL);
603 if (prefix == (char *) NULL) {
604 /* BinReloc not initialized. */
605 if (default_libexec_dir != (const char *) NULL)
606 return strdup (default_libexec_dir);
608 return (char *) NULL;
611 dir = br_build_path (prefix, "libexec");
617 /** Locate the application's configuration files folder.
619 * The path is generated by the following pseudo-code evaluation:
624 * @param default_etc_dir A default path which will used as fallback.
625 * @return A string containing the etc folder's path, which must be freed when
626 * no longer necessary. If BinReloc is not initialized, or if the initialization
627 * function failed, then a copy of default_etc_dir will be returned.
628 * If default_etc_dir is NULL, then NULL will be returned.
631 br_find_etc_dir (const char *default_etc_dir)
635 prefix = br_find_prefix ((const char *) NULL);
636 if (prefix == (char *) NULL) {
637 /* BinReloc not initialized. */
638 if (default_etc_dir != (const char *) NULL)
639 return strdup (default_etc_dir);
641 return (char *) NULL;
644 dir = br_build_path (prefix, "etc");
650 /***********************
652 ***********************/
654 /** Concatenate str1 and str2 to a newly allocated string.
656 * @param str1 A string.
657 * @param str2 Another string.
658 * @returns A newly-allocated string. This string should be freed when no longer needed.
661 br_strcat (const char *str1, const char *str2)
671 len1 = strlen (str1);
672 len2 = strlen (str2);
674 result = (char *) malloc (len1 + len2 + 1);
675 memcpy (result, str1, len1);
676 memcpy (result + len1, str2, len2);
677 result[len1 + len2] = '\0';
684 br_build_path (const char *dir, const char *file)
691 if (len > 0 && dir[len - 1] != '/') {
692 dir2 = br_strcat (dir, "/");
697 result = br_strcat (dir2, file);
704 /* Emulates glibc's strndup() */
706 br_strndup (const char *str, size_t size)
708 char *result = (char *) NULL;
711 if (str == (const char *) NULL)
712 return (char *) NULL;
720 result = (char *) malloc (len + 1);
721 memcpy (result, str, size);
727 /** Extracts the directory component of a path.
729 * Similar to g_dirname() or the dirname commandline application.
733 * br_dirname ("/usr/local/foobar"); --> Returns: "/usr/local"
736 * @param path A path.
737 * @returns A directory name. This string should be freed when no longer needed.
740 br_dirname (const char *path)
744 if (path == (const char *) NULL)
745 return (char *) NULL;
747 end = strrchr (path, '/');
748 if (end == (const char *) NULL)
751 while (end > path && *end == '/')
753 result = br_strndup (path, end - path + 1);
754 if (result[0] == 0) {
764 #endif /* __cplusplus */
766 #endif /* __BINRELOC_C__ */