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