PEBL 2.2
Psychology Experiment Building Language - Cross-platform psychological experiment development system
BinReloc.cpp
Go to the documentation of this file.
1/*
2 * BinReloc - a library for creating relocatable executables
3 * Written by: Hongli Lai <h.lai@chello.nl>
4 * http://autopackage.org/
5 *
6 * This source code is public domain. You can relicense this code
7 * under whatever license you want.
8 *
9 * See http://autopackage.org/docs/binreloc/ for
10 * more information and how to use this.
11 */
12
13#ifndef __BINRELOC_C__
14#define __BINRELOC_C__
15
16#ifdef ENABLE_BINRELOC
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #ifndef _WIN32
20 #include <unistd.h>
21 #endif
22#endif /* ENABLE_BINRELOC */
23#include <stdio.h>
24#include <stdlib.h>
25#include <limits.h>
26#include <string.h>
27#include "BinReloc.h"
28
29#ifdef _WIN32
30 #include <windows.h>
31#endif
32
33#ifdef __cplusplus
34extern "C" {
35#endif /* __cplusplus */
36
37
38
44static char *
45_br_find_exe (BrInitError *error)
46{
47#ifdef _WIN32
48 /* Windows implementation using GetModuleFileName */
49 char *path;
50 DWORD len;
51
52 path = (char *) malloc (MAX_PATH);
53 if (path == NULL) {
54 if (error)
55 *error = BR_INIT_ERROR_NOMEM;
56 return NULL;
57 }
58
59 len = GetModuleFileNameA(NULL, path, MAX_PATH);
60 if (len == 0 || len >= MAX_PATH) {
61 free(path);
62 if (error)
64 return NULL;
65 }
66
67 /* Convert backslashes to forward slashes for consistency */
68 for (DWORD i = 0; i < len; i++) {
69 if (path[i] == '\\')
70 path[i] = '/';
71 }
72
73 return path;
74#elif !defined(ENABLE_BINRELOC)
75 /* BinReloc is disabled */
76 if (error)
78 return NULL;
79#else
80 char *path, *path2, *line, *result;
81 size_t buf_size;
82 ssize_t size;
83 struct stat stat_buf;
84 FILE *f;
85
86 /* Read from /proc/self/exe (symlink) */
87 if (sizeof (path) > SSIZE_MAX)
88 buf_size = SSIZE_MAX - 1;
89 else
90 buf_size = PATH_MAX - 1;
91 path = (char *) malloc (buf_size);
92 if (path == NULL) {
93 /* Cannot allocate memory. */
94 if (error)
95 *error = BR_INIT_ERROR_NOMEM;
96 return NULL;
97 }
98 path2 = (char *) malloc (buf_size);
99 if (path2 == NULL) {
100 /* Cannot allocate memory. */
101 if (error)
102 *error = BR_INIT_ERROR_NOMEM;
103 free (path);
104 return NULL;
105 }
106
107 strncpy (path2, "/proc/self/exe", buf_size - 1);
108
109 while (1) {
110 int i;
111
112 size = readlink (path2, path, buf_size - 1);
113 if (size == -1) {
114 /* Error. */
115 free (path2);
116 break;
117 }
118
119 /* readlink() success. */
120 path[size] = '\0';
121
122 /* Check whether the symlink's target is also a symlink.
123 * We want to get the final target. */
124 i = stat (path, &stat_buf);
125 if (i == -1) {
126 /* Error. */
127 free (path2);
128 break;
129 }
130
131 /* stat() success. */
132 if (!S_ISLNK (stat_buf.st_mode)) {
133 /* path is not a symlink. Done. */
134 free (path2);
135 return path;
136 }
137
138 /* path is a symlink. Continue loop and resolve this. */
139 strncpy (path, path2, buf_size - 1);
140 }
141
142
143 /* readlink() or stat() failed; this can happen when the program is
144 * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
145
146 buf_size = PATH_MAX + 128;
147 line = (char *) realloc (path, buf_size);
148 if (line == NULL) {
149 /* Cannot allocate memory. */
150 free (path);
151 if (error)
152 *error = BR_INIT_ERROR_NOMEM;
153 return NULL;
154 }
155
156 f = fopen ("/proc/self/maps", "r");
157 if (f == NULL) {
158 free (line);
159 if (error)
161 return NULL;
162 }
163
164 /* The first entry should be the executable name. */
165 result = fgets (line, (int) buf_size, f);
166 if (result == NULL) {
167 fclose (f);
168 free (line);
169 if (error)
171 return NULL;
172 }
173
174 /* Get rid of newline character. */
175 buf_size = strlen (line);
176 if (buf_size <= 0) {
177 /* Huh? An empty string? */
178 fclose (f);
179 free (line);
180 if (error)
182 return NULL;
183 }
184 if (line[buf_size - 1] == 10)
185 line[buf_size - 1] = 0;
186
187 /* Extract the filename; it is always an absolute path. */
188 path = strchr (line, '/');
189
190 /* Sanity check. */
191 if (strstr (line, " r-xp ") == NULL || path == NULL) {
192 fclose (f);
193 free (line);
194 if (error)
196 return NULL;
197 }
198
199 path = strdup (path);
200 free (line);
201 fclose (f);
202 return path;
203#endif /* ENABLE_BINRELOC */
204}
205
206
211static char *
212_br_find_exe_for_symbol (const void *symbol, BrInitError *error)
213{
214#ifndef ENABLE_BINRELOC
215 if (error)
216 *error = BR_INIT_ERROR_DISABLED;
217 return (char *) NULL;
218#else
219 #define SIZE PATH_MAX + 100
220 FILE *f;
221 size_t address_string_len;
222 char *address_string, line[SIZE], *found;
223
224 if (symbol == NULL)
225 return (char *) NULL;
226
227 f = fopen ("/proc/self/maps", "r");
228 if (f == NULL)
229 return (char *) NULL;
230
231 address_string_len = 4;
232 address_string = (char *) malloc (address_string_len);
233 found = (char *) NULL;
234
235 while (!feof (f)) {
236 char *start_addr, *end_addr, *end_addr_end, *file;
237 void *start_addr_p, *end_addr_p;
238 size_t len;
239
240 if (fgets (line, SIZE, f) == NULL)
241 break;
242
243 /* Sanity check. */
244 if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
245 continue;
246
247 /* Parse line. */
248 start_addr = line;
249 end_addr = strchr (line, '-');
250 file = strchr (line, '/');
251
252 /* More sanity check. */
253 if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
254 continue;
255
256 end_addr[0] = '\0';
257 end_addr++;
258 end_addr_end = strchr (end_addr, ' ');
259 if (end_addr_end == NULL)
260 continue;
261
262 end_addr_end[0] = '\0';
263 len = strlen (file);
264 if (len == 0)
265 continue;
266 if (file[len - 1] == '\n')
267 file[len - 1] = '\0';
268
269 /* Get rid of "(deleted)" from the filename. */
270 len = strlen (file);
271 if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
272 file[len - 10] = '\0';
273
274 /* I don't know whether this can happen but better safe than sorry. */
275 len = strlen (start_addr);
276 if (len != strlen (end_addr))
277 continue;
278
279
280 /* Transform the addresses into a string in the form of 0xdeadbeef,
281 * then transform that into a pointer. */
282 if (address_string_len < len + 3) {
283 address_string_len = len + 3;
284 address_string = (char *) realloc (address_string, address_string_len);
285 }
286
287 memcpy (address_string, "0x", 2);
288 memcpy (address_string + 2, start_addr, len);
289 address_string[2 + len] = '\0';
290 sscanf (address_string, "%p", &start_addr_p);
291
292 memcpy (address_string, "0x", 2);
293 memcpy (address_string + 2, end_addr, len);
294 address_string[2 + len] = '\0';
295 sscanf (address_string, "%p", &end_addr_p);
296
297
298 if (symbol >= start_addr_p && symbol < end_addr_p) {
299 found = file;
300 break;
301 }
302 }
303
304 free (address_string);
305 fclose (f);
306
307 if (found == NULL)
308 return (char *) NULL;
309 else
310 return strdup (found);
311#endif /* ENABLE_BINRELOC */
312}
313
314
315#ifndef BINRELOC_RUNNING_DOXYGEN
316 #undef NULL
317 #define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */
318#endif
319
320static char *exe = (char *) NULL;
321
322
337int
339{
340 exe = _br_find_exe (error);
341 return exe != NULL;
342}
343
344
359int
361{
362 exe = _br_find_exe_for_symbol ((const void *) "", error);
363 return exe != NULL;
364}
365
366
376char *
377br_find_exe (const char *default_exe)
378{
379 if (exe == (char *) NULL) {
380 /* BinReloc is not initialized. */
381 if (default_exe != (const char *) NULL)
382 return strdup (default_exe);
383 else
384 return (char *) NULL;
385 }
386 return strdup (exe);
387}
388
389
404char *
405br_find_exe_dir (const char *default_dir)
406{
407 if (exe == NULL) {
408 /* BinReloc not initialized. */
409 if (default_dir != NULL)
410 return strdup (default_dir);
411 else
412 return (char*)NULL;
413 }
414
415 return br_dirname (exe);
416}
417
418
432char *
433br_find_prefix (const char *default_prefix)
434{
435 char *dir1, *dir2;
436
437 if (exe == (char *) NULL) {
438 /* BinReloc not initialized. */
439 if (default_prefix != (const char *) NULL)
440 return strdup (default_prefix);
441 else
442 return (char *) NULL;
443 }
444
445 dir1 = br_dirname (exe);
446 dir2 = br_dirname (dir1);
447 free (dir1);
448 return dir2;
449}
450
451
465char *
466br_find_bin_dir (const char *default_bin_dir)
467{
468 char *prefix, *dir;
469
470 prefix = br_find_prefix ((const char *) NULL);
471 if (prefix == (char *) NULL) {
472 /* BinReloc not initialized. */
473 if (default_bin_dir != (const char *) NULL)
474 return strdup (default_bin_dir);
475 else
476 return (char *) NULL;
477 }
478
479 dir = br_build_path (prefix, "bin");
480 free (prefix);
481 return dir;
482}
483
484
498char *
499br_find_sbin_dir (const char *default_sbin_dir)
500{
501 char *prefix, *dir;
502
503 prefix = br_find_prefix ((const char *) NULL);
504 if (prefix == (char *) NULL) {
505 /* BinReloc not initialized. */
506 if (default_sbin_dir != (const char *) NULL)
507 return strdup (default_sbin_dir);
508 else
509 return (char *) NULL;
510 }
511
512 dir = br_build_path (prefix, "sbin");
513 free (prefix);
514 return dir;
515}
516
517
532char *
533br_find_data_dir (const char *default_data_dir)
534{
535 char *prefix, *dir;
536
537 prefix = br_find_prefix ((const char *) NULL);
538 if (prefix == (char *) NULL) {
539 /* BinReloc not initialized. */
540 if (default_data_dir != (const char *) NULL)
541 return strdup (default_data_dir);
542 else
543 return (char *) NULL;
544 }
545
546 dir = br_build_path (prefix, "share");
547 free (prefix);
548 return dir;
549}
550
551
565char *
566br_find_locale_dir (const char *default_locale_dir)
567{
568 char *data_dir, *dir;
569
570 data_dir = br_find_data_dir ((const char *) NULL);
571 if (data_dir == (char *) NULL) {
572 /* BinReloc not initialized. */
573 if (default_locale_dir != (const char *) NULL)
574 return strdup (default_locale_dir);
575 else
576 return (char *) NULL;
577 }
578
579 dir = br_build_path (data_dir, "locale");
580 free (data_dir);
581 return dir;
582}
583
584
598char *
599br_find_lib_dir (const char *default_lib_dir)
600{
601 char *prefix, *dir;
602
603 prefix = br_find_prefix ((const char *) NULL);
604 if (prefix == (char *) NULL) {
605 /* BinReloc not initialized. */
606 if (default_lib_dir != (const char *) NULL)
607 return strdup (default_lib_dir);
608 else
609 return (char *) NULL;
610 }
611
612 dir = br_build_path (prefix, "lib");
613 free (prefix);
614 return dir;
615}
616
617
631char *
632br_find_libexec_dir (const char *default_libexec_dir)
633{
634 char *prefix, *dir;
635
636 prefix = br_find_prefix ((const char *) NULL);
637 if (prefix == (char *) NULL) {
638 /* BinReloc not initialized. */
639 if (default_libexec_dir != (const char *) NULL)
640 return strdup (default_libexec_dir);
641 else
642 return (char *) NULL;
643 }
644
645 dir = br_build_path (prefix, "libexec");
646 free (prefix);
647 return dir;
648}
649
650
664char *
665br_find_etc_dir (const char *default_etc_dir)
666{
667 char *prefix, *dir;
668
669 prefix = br_find_prefix ((const char *) NULL);
670 if (prefix == (char *) NULL) {
671 /* BinReloc not initialized. */
672 if (default_etc_dir != (const char *) NULL)
673 return strdup (default_etc_dir);
674 else
675 return (char *) NULL;
676 }
677
678 dir = br_build_path (prefix, "etc");
679 free (prefix);
680 return dir;
681}
682
683
684/***********************
685 * Utility functions
686 ***********************/
687
694char *
695br_strcat (const char *str1, const char *str2)
696{
697 char *result;
698 size_t len1, len2;
699
700 if (str1 == NULL)
701 str1 = "";
702 if (str2 == NULL)
703 str2 = "";
704
705 len1 = strlen (str1);
706 len2 = strlen (str2);
707
708 result = (char *) malloc (len1 + len2 + 1);
709 memcpy (result, str1, len1);
710 memcpy (result + len1, str2, len2);
711 result[len1 + len2] = '\0';
712
713 return result;
714}
715
716
717char *
718br_build_path (const char *dir, const char *file)
719{
720 char *dir2, *result;
721 size_t len;
722 int must_free = 0;
723
724 len = strlen (dir);
725 if (len > 0 && dir[len - 1] != '/') {
726 dir2 = br_strcat (dir, "/");
727 must_free = 1;
728 } else
729 dir2 = (char *) dir;
730
731 result = br_strcat (dir2, file);
732 if (must_free)
733 free (dir2);
734 return result;
735}
736
737
738/* Emulates glibc's strndup() */
739static char *
740br_strndup (const char *str, size_t size)
741{
742 char *result = (char *) NULL;
743 size_t len;
744
745 if (str == (const char *) NULL)
746 return (char *) NULL;
747
748 len = strlen (str);
749 if (len == 0)
750 return strdup ("");
751 if (size > len)
752 size = len;
753
754 result = (char *) malloc (len + 1);
755 memcpy (result, str, size);
756 result[size] = '\0';
757 return result;
758}
759
760
775char *
776br_dirname (const char *path)
777{
778 const char *end;
779 char * result;
780
781 if (path == (const char *) NULL)
782 return (char *) NULL;
783
784 end = strrchr (path, '/');
785 if (end == (const char *) NULL)
786 return strdup (".");
787
788 while (end > path && *end == '/')
789 end--;
790 result = br_strndup (path, end - path + 1);
791 if (result[0] == 0) {
792 free (result);
793 return strdup ("/");
794 } else
795 return result;
796}
797
798
799#ifdef __cplusplus
800}
801#endif /* __cplusplus */
802
803#endif /* __BINRELOC_C__ */
char * br_find_prefix(const char *default_prefix)
Definition BinReloc.cpp:433
#define NULL
Definition BinReloc.cpp:317
char * br_find_bin_dir(const char *default_bin_dir)
Definition BinReloc.cpp:466
char * br_dirname(const char *path)
Definition BinReloc.cpp:776
char * br_find_locale_dir(const char *default_locale_dir)
Definition BinReloc.cpp:566
int br_init(BrInitError *error)
Definition BinReloc.cpp:338
char * br_find_sbin_dir(const char *default_sbin_dir)
Definition BinReloc.cpp:499
char * br_strcat(const char *str1, const char *str2)
Definition BinReloc.cpp:695
char * br_find_exe_dir(const char *default_dir)
Definition BinReloc.cpp:405
int br_init_lib(BrInitError *error)
Definition BinReloc.cpp:360
char * br_find_exe(const char *default_exe)
Definition BinReloc.cpp:377
char * br_find_lib_dir(const char *default_lib_dir)
Definition BinReloc.cpp:599
char * br_find_etc_dir(const char *default_etc_dir)
Definition BinReloc.cpp:665
char * br_build_path(const char *dir, const char *file)
Definition BinReloc.cpp:718
char * br_find_data_dir(const char *default_data_dir)
Definition BinReloc.cpp:533
char * br_find_libexec_dir(const char *default_libexec_dir)
Definition BinReloc.cpp:632
BrInitError
Definition BinReloc.h:22
@ BR_INIT_ERROR_OPEN_MAPS
Definition BinReloc.h:26
@ BR_INIT_ERROR_DISABLED
Definition BinReloc.h:32
@ BR_INIT_ERROR_NOMEM
Definition BinReloc.h:24
@ BR_INIT_ERROR_INVALID_MAPS
Definition BinReloc.h:30
@ BR_INIT_ERROR_READ_MAPS
Definition BinReloc.h:28