gwenhywfar 5.14.1
binreloc.c
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#include "config.h"
17
18#include <gwenhywfar/gwenhywfarapi.h>
19
20
21#ifdef ENABLE_BINRELOC
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#endif /* ENABLE_BINRELOC */
26#include <stdio.h>
27#include <stdlib.h>
28#include <limits.h>
29#include <string.h>
30#include "binreloc.h"
31
32#ifdef __cplusplus
33extern "C" {
34#endif /* __cplusplus */
35
36
37#ifdef OS_WIN32
38# define DIRSEP "\\"
39# define DIRSEP_C '\\'
40#else
41# define DIRSEP "/"
42# define DIRSEP_C '/'
43#endif
44
50static char *
52{
53#ifndef ENABLE_BINRELOC
54 if (error)
56 return NULL;
57#else
58 char *path, *path2, *line, *result;
59 size_t buf_size;
60 ssize_t size;
61 struct stat stat_buf;
62 FILE *f;
63
64 /* Read from /proc/self/exe (symlink) */
65 if (sizeof(path) > SSIZE_MAX)
66 buf_size = SSIZE_MAX - 1;
67 else
68 buf_size = PATH_MAX - 1;
69 path = (char *) malloc(buf_size);
70 if (path == NULL) {
71 /* Cannot allocate memory. */
72 if (error)
73 *error = BR_INIT_ERROR_NOMEM;
74 return NULL;
75 }
76 path2 = (char *) malloc(buf_size);
77 if (path2 == NULL) {
78 /* Cannot allocate memory. */
79 if (error)
80 *error = BR_INIT_ERROR_NOMEM;
81 free(path);
82 return NULL;
83 }
84
85 strncpy(path2, "/proc/self/exe", buf_size - 1);
86
87 while (1) {
88 int i;
89
90 size = readlink(path2, path, buf_size - 1);
91 if (size == -1) {
92 /* Error. */
93 free(path2);
94 break;
95 }
96
97 /* readlink() success. */
98 path[size] = '\0';
99
100 /* Check whether the symlink's target is also a symlink.
101 * We want to get the final target. */
102 i = stat(path, &stat_buf);
103 if (i == -1) {
104 /* Error. */
105 free(path2);
106 break;
107 }
108
109 /* stat() success. */
110 if (!S_ISLNK(stat_buf.st_mode)) {
111 /* path is not a symlink. Done. */
112 free(path2);
113 return path;
114 }
115
116 /* path is a symlink. Continue loop and resolve this. */
117 strncpy(path, path2, buf_size - 1);
118 }
119
120
121 /* readlink() or stat() failed; this can happen when the program is
122 * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
123
124 buf_size = PATH_MAX + 128;
125 line = (char *) realloc(path, buf_size);
126 if (line == NULL) {
127 /* Cannot allocate memory. */
128 free(path);
129 if (error)
130 *error = BR_INIT_ERROR_NOMEM;
131 return NULL;
132 }
133
134 f = fopen("/proc/self/maps", "r");
135 if (f == NULL) {
136 free(line);
137 if (error)
139 return NULL;
140 }
141
142 /* The first entry should be the executable name. */
143 result = fgets(line, (int) buf_size, f);
144 if (result == NULL) {
145 fclose(f);
146 free(line);
147 if (error)
149 return NULL;
150 }
151
152 /* Get rid of newline character. */
153 buf_size = strlen(line);
154 if (buf_size <= 0) {
155 /* Huh? An empty string? */
156 fclose(f);
157 free(line);
158 if (error)
160 return NULL;
161 }
162 if (line[buf_size - 1] == 10)
163 line[buf_size - 1] = 0;
164
165 /* Extract the filename; it is always an absolute path. */
166 path = strchr(line, DIRSEP_C);
167
168 /* Sanity check. */
169 if (strstr(line, " r-xp ") == NULL || path == NULL) {
170 fclose(f);
171 free(line);
172 if (error)
174 return NULL;
175 }
176
177 path = strdup(path);
178 free(line);
179 fclose(f);
180 return path;
181#endif /* ENABLE_BINRELOC */
182}
183
184
189static char *
191{
192#ifndef ENABLE_BINRELOC
193 if (error)
194 *error = BR_INIT_ERROR_DISABLED;
195 return (char *) NULL;
196#else
197#define SIZE PATH_MAX + 100
198 FILE *f;
199 size_t address_string_len;
200 char *address_string, line[SIZE], *found;
201
202 if (symbol == NULL)
203 return (char *) NULL;
204
205 f = fopen("/proc/self/maps", "r");
206 if (f == NULL)
207 return (char *) NULL;
208
209 address_string_len = 4;
210 address_string = (char *) malloc(address_string_len);
211 found = (char *) NULL;
212
213 while (!feof(f)) {
214 char *start_addr, *end_addr, *end_addr_end, *file;
215 void *start_addr_p, *end_addr_p;
216 size_t len;
217
218 if (fgets(line, SIZE, f) == NULL)
219 break;
220
221 /* Sanity check. */
222 if (strstr(line, " r-xp ") == NULL || strchr(line, '/') == NULL)
223 continue;
224
225 /* Parse line. */
226 start_addr = line;
227 end_addr = strchr(line, '-');
228 file = strchr(line, '/');
229
230 /* More sanity check. */
231 if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
232 continue;
233
234 end_addr[0] = '\0';
235 end_addr++;
236 end_addr_end = strchr(end_addr, ' ');
237 if (end_addr_end == NULL)
238 continue;
239
240 end_addr_end[0] = '\0';
241 len = strlen(file);
242 if (len == 0)
243 continue;
244 if (file[len - 1] == '\n')
245 file[len - 1] = '\0';
246
247 /* Get rid of "(deleted)" from the filename. */
248 len = strlen(file);
249 if (len > 10 && strcmp(file + len - 10, " (deleted)") == 0)
250 file[len - 10] = '\0';
251
252 /* I don't know whether this can happen but better safe than sorry. */
253 len = strlen(start_addr);
254 if (len != strlen(end_addr))
255 continue;
256
257
258 /* Transform the addresses into a string in the form of 0xdeadbeef,
259 * then transform that into a pointer. */
260 if (address_string_len < len + 3) {
261 char *tmp_address_string;
262
263 address_string_len = len + 3;
264 tmp_address_string = (char *) realloc(address_string, address_string_len);
265 if (tmp_address_string==NULL)
266 free(address_string);
267 address_string = tmp_address_string;
268 }
269
270 memcpy(address_string, "0x", 2);
271 memcpy(address_string + 2, start_addr, len);
272 address_string[2 + len] = '\0';
273 sscanf(address_string, "%p", &start_addr_p);
274
275 memcpy(address_string, "0x", 2);
276 memcpy(address_string + 2, end_addr, len);
277 address_string[2 + len] = '\0';
278 sscanf(address_string, "%p", &end_addr_p);
279
280
281 if (symbol >= start_addr_p && symbol < end_addr_p) {
282 found = file;
283 break;
284 }
285 }
286
287 free(address_string);
288 fclose(f);
289
290 if (found == NULL)
291 return (char *) NULL;
292 else
293 return strdup(found);
294#endif /* ENABLE_BINRELOC */
295}
296
297
298#ifndef BINRELOC_RUNNING_DOXYGEN
299#undef NULL
300#define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */
301#endif
302
303static char *exe = (char *) NULL;
304
305
320int
322{
323 exe = _br_find_exe(error);
324 return exe != NULL;
325}
326
327
342int
344{
346 return exe != NULL;
347}
348
349
359char *
360br_find_exe(const char *default_exe)
361{
362 if (exe == (char *) NULL) {
363 /* BinReloc is not initialized. */
364 if (default_exe != (const char *) NULL)
365 return strdup(default_exe);
366 else
367 return (char *) NULL;
368 }
369 return strdup(exe);
370}
371
372
387char *
388br_find_exe_dir(const char *default_dir)
389{
390 if (exe == NULL) {
391 /* BinReloc not initialized. */
392 if (default_dir != NULL)
393 return strdup(default_dir);
394 else
395 return NULL;
396 }
397
398 return br_dirname(exe);
399}
400
401
415char *
416br_find_prefix(const char *default_prefix)
417{
418 char *dir1, *dir2;
419
420 if (exe == (char *) NULL) {
421 /* BinReloc not initialized. */
422 if (default_prefix != (const char *) NULL)
423 return strdup(default_prefix);
424 else
425 return (char *) NULL;
426 }
427
428 dir1 = br_dirname(exe);
429 dir2 = br_dirname(dir1);
430 free(dir1);
431 return dir2;
432}
433
434
448char *
449br_find_bin_dir(const char *default_bin_dir)
450{
451 char *prefix, *dir;
452
453 prefix = br_find_prefix((const char *) NULL);
454 if (prefix == (char *) NULL) {
455 /* BinReloc not initialized. */
456 if (default_bin_dir != (const char *) NULL)
457 return strdup(default_bin_dir);
458 else
459 return (char *) NULL;
460 }
461
462 dir = br_build_path(prefix, "bin");
463 free(prefix);
464 return dir;
465}
466
467
481char *
482br_find_sbin_dir(const char *default_sbin_dir)
483{
484 char *prefix, *dir;
485
486 prefix = br_find_prefix((const char *) NULL);
487 if (prefix == (char *) NULL) {
488 /* BinReloc not initialized. */
489 if (default_sbin_dir != (const char *) NULL)
490 return strdup(default_sbin_dir);
491 else
492 return (char *) NULL;
493 }
494
495 dir = br_build_path(prefix, "sbin");
496 free(prefix);
497 return dir;
498}
499
500
515char *
516br_find_data_dir(const char *default_data_dir)
517{
518 char *prefix, *dir;
519
520 prefix = br_find_prefix((const char *) NULL);
521 if (prefix == (char *) NULL) {
522 /* BinReloc not initialized. */
523 if (default_data_dir != (const char *) NULL)
524 return strdup(default_data_dir);
525 else
526 return (char *) NULL;
527 }
528
529 dir = br_build_path(prefix, "share");
530 free(prefix);
531 return dir;
532}
533
534
548char *
549br_find_locale_dir(const char *default_locale_dir)
550{
551 char *data_dir, *dir;
552
553 data_dir = br_find_data_dir((const char *) NULL);
554 if (data_dir == (char *) NULL) {
555 /* BinReloc not initialized. */
556 if (default_locale_dir != (const char *) NULL)
557 return strdup(default_locale_dir);
558 else
559 return (char *) NULL;
560 }
561
562 dir = br_build_path(data_dir, "locale");
563 free(data_dir);
564 return dir;
565}
566
567
581char *
582br_find_lib_dir(const char *default_lib_dir)
583{
584 char *prefix, *dir;
585
586 prefix = br_find_prefix((const char *) NULL);
587 if (prefix == (char *) NULL) {
588 /* BinReloc not initialized. */
589 if (default_lib_dir != (const char *) NULL)
590 return strdup(default_lib_dir);
591 else
592 return (char *) NULL;
593 }
594
595 dir = br_build_path(prefix, "lib");
596 free(prefix);
597 return dir;
598}
599
600
614char *
615br_find_libexec_dir(const char *default_libexec_dir)
616{
617 char *prefix, *dir;
618
619 prefix = br_find_prefix((const char *) NULL);
620 if (prefix == (char *) NULL) {
621 /* BinReloc not initialized. */
622 if (default_libexec_dir != (const char *) NULL)
623 return strdup(default_libexec_dir);
624 else
625 return (char *) NULL;
626 }
627
628 dir = br_build_path(prefix, "libexec");
629 free(prefix);
630 return dir;
631}
632
633
647char *
648br_find_etc_dir(const char *default_etc_dir)
649{
650 char *prefix, *dir;
651
652 prefix = br_find_prefix((const char *) NULL);
653 if (prefix == (char *) NULL) {
654 /* BinReloc not initialized. */
655 if (default_etc_dir != (const char *) NULL)
656 return strdup(default_etc_dir);
657 else
658 return (char *) NULL;
659 }
660
661 dir = br_build_path(prefix, "etc");
662 free(prefix);
663 return dir;
664}
665
666
667/***********************
668 * Utility functions
669 ***********************/
670
677char *
678br_strcat(const char *str1, const char *str2)
679{
680 char *result;
681 size_t len1, len2;
682
683 if (str1 == NULL)
684 str1 = "";
685 if (str2 == NULL)
686 str2 = "";
687
688 len1 = strlen(str1);
689 len2 = strlen(str2);
690
691 result = (char *) malloc(len1 + len2 + 1);
692 memcpy(result, str1, len1);
693 memcpy(result + len1, str2, len2);
694 result[len1 + len2] = '\0';
695
696 return result;
697}
698
699
700char *
701br_build_path(const char *dir, const char *file)
702{
703 char *dir2, *result;
704 size_t len;
705 int must_free = 0;
706
707 len = strlen(dir);
708 if (len > 0 && dir[len - 1] != DIRSEP_C) {
709 dir2 = br_strcat(dir, DIRSEP);
710 must_free = 1;
711 }
712 else
713 dir2 = (char *) dir;
714
715 result = br_strcat(dir2, file);
716 if (must_free)
717 free(dir2);
718 return result;
719}
720
721
722/* Emulates glibc's strndup() */
723static char *
724br_strndup(const char *str, size_t size)
725{
726 char *result = (char *) NULL;
727 size_t len;
728
729 if (str == (const char *) NULL)
730 return (char *) NULL;
731
732 len = strlen(str);
733 if (len == 0)
734 return strdup("");
735 if (size > len)
736 size = len;
737
738 result = (char *) malloc(len + 1);
739 memcpy(result, str, size);
740 result[size] = '\0';
741 return result;
742}
743
744
757char *
758br_dirname(const char *path)
759{
760 char *end, *result;
761
762 if (path == (const char *) NULL)
763 return (char *) NULL;
764
765 end = strrchr(path, DIRSEP_C);
766 if (end == (const char *) NULL)
767 return strdup(".");
768
769 while (end > path && *end == DIRSEP_C)
770 end--;
771 result = br_strndup(path, end - path + 1);
772 if (result[0] == 0) {
773 free(result);
774 return strdup(DIRSEP);
775 }
776 else
777 return result;
778}
779
780
781#ifdef __cplusplus
782}
783#endif /* __cplusplus */
784
785#endif /* __BINRELOC_C__ */
static char * br_strndup(const char *str, size_t size)
Definition binreloc.c:724
#define NULL
Definition binreloc.c:300
#define DIRSEP_C
Definition binreloc.c:42
#define DIRSEP
Definition binreloc.c:41
static char * _br_find_exe(BrInitError *error)
Definition binreloc.c:51
static char * exe
Definition binreloc.c:303
static char * _br_find_exe_for_symbol(const void *symbol, GWEN_UNUSED BrInitError *error)
Definition binreloc.c:190
#define br_find_etc_dir
Definition binreloc.h:50
#define br_find_sbin_dir
Definition binreloc.h:45
#define br_find_bin_dir
Definition binreloc.h:44
#define br_find_prefix
Definition binreloc.h:43
#define br_find_data_dir
Definition binreloc.h:46
#define br_find_libexec_dir
Definition binreloc.h:49
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
#define br_find_exe_dir
Definition binreloc.h:42
#define br_build_path
Definition binreloc.h:52
#define br_init
Definition binreloc.h:39
#define br_strcat
Definition binreloc.h:51
#define br_dirname
Definition binreloc.h:53
#define br_find_exe
Definition binreloc.h:41
#define br_find_lib_dir
Definition binreloc.h:48
#define br_init_lib
Definition binreloc.h:40
#define br_find_locale_dir
Definition binreloc.h:47
#define GWEN_UNUSED