Line data Source code
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 : #include <unistd.h>
20 : #endif /* ENABLE_BINRELOC */
21 : #include <stdio.h>
22 : #include <stdlib.h>
23 : #include <limits.h>
24 : #include <string.h>
25 : #include "binreloc.h"
26 :
27 : #ifdef __cplusplus
28 : extern "C" {
29 : #endif /* __cplusplus */
30 :
31 : extern char* strdup(const char*);
32 :
33 : /** @internal
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.
37 : */
38 : static char *
39 : _br_find_exe (BrInitError *error)
40 : {
41 : #ifndef ENABLE_BINRELOC
42 0 : if (error)
43 0 : *error = BR_INIT_ERROR_DISABLED;
44 0 : return NULL;
45 : #else
46 : char *path, *path2, *line, *result;
47 : size_t buf_size;
48 : ssize_t size;
49 : struct stat stat_buf;
50 : FILE *f;
51 :
52 : /* Read from /proc/self/exe (symlink) */
53 : if (sizeof (path) > SSIZE_MAX)
54 : buf_size = SSIZE_MAX - 1;
55 : else
56 : buf_size = PATH_MAX - 1;
57 : path = (char *) malloc (buf_size);
58 : if (path == NULL) {
59 : /* Cannot allocate memory. */
60 : if (error)
61 : *error = BR_INIT_ERROR_NOMEM;
62 : return NULL;
63 : }
64 : path2 = (char *) malloc (buf_size);
65 : if (path2 == NULL) {
66 : /* Cannot allocate memory. */
67 : if (error)
68 : *error = BR_INIT_ERROR_NOMEM;
69 : free (path);
70 : return NULL;
71 : }
72 :
73 : strncpy (path2, "/proc/self/exe", buf_size - 1);
74 :
75 : while (1) {
76 : int i;
77 :
78 : size = readlink (path2, path, buf_size - 1);
79 : if (size == -1) {
80 : /* Error. */
81 : free (path2);
82 : break;
83 : }
84 :
85 : /* readlink() success. */
86 : path[size] = '\0';
87 :
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);
91 : if (i == -1) {
92 : /* Error. */
93 : free (path2);
94 : break;
95 : }
96 :
97 : /* stat() success. */
98 : if (!S_ISLNK (stat_buf.st_mode)) {
99 : /* path is not a symlink. Done. */
100 : free (path2);
101 : return path;
102 : }
103 :
104 : /* path is a symlink. Continue loop and resolve this. */
105 : strncpy (path, path2, buf_size - 1);
106 : }
107 :
108 :
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. */
111 :
112 : buf_size = PATH_MAX + 128;
113 : line = (char *) realloc (path, buf_size);
114 : if (line == NULL) {
115 : /* Cannot allocate memory. */
116 : free (path);
117 : if (error)
118 : *error = BR_INIT_ERROR_NOMEM;
119 : return NULL;
120 : }
121 :
122 : f = fopen ("/proc/self/maps", "r");
123 : if (f == NULL) {
124 : free (line);
125 : if (error)
126 : *error = BR_INIT_ERROR_OPEN_MAPS;
127 : return NULL;
128 : }
129 :
130 : /* The first entry should be the executable name. */
131 : result = fgets (line, (int) buf_size, f);
132 : if (result == NULL) {
133 : fclose (f);
134 : free (line);
135 : if (error)
136 : *error = BR_INIT_ERROR_READ_MAPS;
137 : return NULL;
138 : }
139 :
140 : /* Get rid of newline character. */
141 : buf_size = strlen (line);
142 : if (buf_size <= 0) {
143 : /* Huh? An empty string? */
144 : fclose (f);
145 : free (line);
146 : if (error)
147 : *error = BR_INIT_ERROR_INVALID_MAPS;
148 : return NULL;
149 : }
150 : if (line[buf_size - 1] == 10)
151 : line[buf_size - 1] = 0;
152 :
153 : /* Extract the filename; it is always an absolute path. */
154 : path = strchr (line, '/');
155 :
156 : /* Sanity check. */
157 : if (strstr (line, " r-xp ") == NULL || path == NULL) {
158 : fclose (f);
159 : free (line);
160 : if (error)
161 : *error = BR_INIT_ERROR_INVALID_MAPS;
162 : return NULL;
163 : }
164 :
165 : path = strdup (path);
166 : free (line);
167 : fclose (f);
168 : return path;
169 : #endif /* ENABLE_BINRELOC */
170 : }
171 :
172 :
173 : /** @internal
174 : * Find the canonical filename of the executable which owns symbol.
175 : * Returns a filename which must be freed, or NULL on error.
176 : */
177 : static char *
178 : _br_find_exe_for_symbol (const void *symbol, BrInitError *error)
179 : {
180 : #ifndef ENABLE_BINRELOC
181 0 : if (error)
182 0 : *error = BR_INIT_ERROR_DISABLED;
183 0 : return (char *) NULL;
184 : #else
185 : #define SIZE PATH_MAX + 100
186 : FILE *f;
187 : size_t address_string_len;
188 : char *address_string, line[SIZE], *found;
189 :
190 : if (symbol == NULL)
191 : return (char *) NULL;
192 :
193 : f = fopen ("/proc/self/maps", "r");
194 : if (f == NULL)
195 : return (char *) NULL;
196 :
197 : address_string_len = 4;
198 : address_string = (char *) malloc (address_string_len);
199 : found = (char *) NULL;
200 :
201 : while (!feof (f)) {
202 : char *start_addr, *end_addr, *end_addr_end, *file;
203 : void *start_addr_p, *end_addr_p;
204 : size_t len;
205 :
206 : if (fgets (line, SIZE, f) == NULL)
207 : break;
208 :
209 : /* Sanity check. */
210 : if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
211 : continue;
212 :
213 : /* Parse line. */
214 : start_addr = line;
215 : end_addr = strchr (line, '-');
216 : file = strchr (line, '/');
217 :
218 : /* More sanity check. */
219 : if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
220 : continue;
221 :
222 : end_addr[0] = '\0';
223 : end_addr++;
224 : end_addr_end = strchr (end_addr, ' ');
225 : if (end_addr_end == NULL)
226 : continue;
227 :
228 : end_addr_end[0] = '\0';
229 : len = strlen (file);
230 : if (len == 0)
231 : continue;
232 : if (file[len - 1] == '\n')
233 : file[len - 1] = '\0';
234 :
235 : /* Get rid of "(deleted)" from the filename. */
236 : len = strlen (file);
237 : if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
238 : file[len - 10] = '\0';
239 :
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))
243 : continue;
244 :
245 :
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);
251 : }
252 :
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);
257 :
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);
262 :
263 :
264 : if (symbol >= start_addr_p && symbol < end_addr_p) {
265 : found = file;
266 : break;
267 : }
268 : }
269 :
270 : free (address_string);
271 : fclose (f);
272 :
273 : if (found == NULL)
274 : return (char *) NULL;
275 : else
276 : return strdup (found);
277 : #endif /* ENABLE_BINRELOC */
278 : }
279 :
280 :
281 : #ifndef BINRELOC_RUNNING_DOXYGEN
282 : #undef NULL
283 : #define NULL ((void *) 0) /* typecasted as char* for C++ type safeness */
284 : #endif
285 :
286 : static char *exe = (char *) NULL;
287 :
288 :
289 : /** Initialize the BinReloc library (for applications).
290 : *
291 : * This function must be called before using any other BinReloc functions.
292 : * It attempts to locate the application's canonical filename.
293 : *
294 : * @note If you want to use BinReloc for a library, then you should call
295 : * br_init_lib() instead.
296 : *
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.
300 : *
301 : * @returns 1 on success, 0 if BinReloc failed to initialize.
302 : */
303 : int
304 : br_init (BrInitError *error)
305 : {
306 0 : exe = _br_find_exe (error);
307 0 : return exe != NULL;
308 : }
309 :
310 :
311 : /** Initialize the BinReloc library (for libraries).
312 : *
313 : * This function must be called before using any other BinReloc functions.
314 : * It attempts to locate the calling library's canonical filename.
315 : *
316 : * @note The BinReloc source code MUST be included in your library, or this
317 : * function won't work correctly.
318 : *
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.
322 : *
323 : * @returns 1 on success, 0 if a filename cannot be found.
324 : */
325 : int
326 : br_init_lib (BrInitError *error)
327 : {
328 0 : exe = _br_find_exe_for_symbol ((const void *) "", error);
329 0 : return exe != NULL;
330 : }
331 :
332 :
333 : /** Find the canonical filename of the current application.
334 : *
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.
341 : */
342 : char *
343 : br_find_exe (const char *default_exe)
344 : {
345 0 : if (exe == (char *) NULL) {
346 : /* BinReloc is not initialized. */
347 0 : if (default_exe != (const char *) NULL)
348 0 : return strdup (default_exe);
349 : else
350 0 : return (char *) NULL;
351 : }
352 0 : return strdup (exe);
353 0 : }
354 :
355 :
356 : /** Locate the directory in which the current application is installed.
357 : *
358 : * The prefix is generated by the following pseudo-code evaluation:
359 : * \code
360 : * dirname(exename)
361 : * \endcode
362 : *
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
368 : * returned.
369 : */
370 : char *
371 : br_find_exe_dir (const char *default_dir)
372 : {
373 0 : if (exe == NULL) {
374 : /* BinReloc not initialized. */
375 0 : if (default_dir != NULL)
376 0 : return strdup (default_dir);
377 : else
378 0 : return NULL;
379 : }
380 :
381 0 : return br_dirname (exe);
382 0 : }
383 :
384 :
385 : /** Locate the prefix in which the current application is installed.
386 : *
387 : * The prefix is generated by the following pseudo-code evaluation:
388 : * \code
389 : * dirname(dirname(exename))
390 : * \endcode
391 : *
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.
397 : */
398 : char *
399 : br_find_prefix (const char *default_prefix)
400 : {
401 : char *dir1, *dir2;
402 :
403 0 : if (exe == (char *) NULL) {
404 : /* BinReloc not initialized. */
405 0 : if (default_prefix != (const char *) NULL)
406 0 : return strdup (default_prefix);
407 : else
408 0 : return (char *) NULL;
409 : }
410 :
411 0 : dir1 = br_dirname (exe);
412 0 : dir2 = br_dirname (dir1);
413 0 : free (dir1);
414 0 : return dir2;
415 0 : }
416 :
417 :
418 : /** Locate the application's binary folder.
419 : *
420 : * The path is generated by the following pseudo-code evaluation:
421 : * \code
422 : * prefix + "/bin"
423 : * \endcode
424 : *
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.
430 : */
431 : char *
432 : br_find_bin_dir (const char *default_bin_dir)
433 : {
434 : char *prefix, *dir;
435 :
436 0 : prefix = br_find_prefix ((const char *) NULL);
437 0 : if (prefix == (char *) NULL) {
438 : /* BinReloc not initialized. */
439 0 : if (default_bin_dir != (const char *) NULL)
440 0 : return strdup (default_bin_dir);
441 : else
442 0 : return (char *) NULL;
443 : }
444 :
445 0 : dir = br_build_path (prefix, "bin");
446 0 : free (prefix);
447 0 : return dir;
448 0 : }
449 :
450 :
451 : /** Locate the application's superuser binary folder.
452 : *
453 : * The path is generated by the following pseudo-code evaluation:
454 : * \code
455 : * prefix + "/sbin"
456 : * \endcode
457 : *
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.
463 : */
464 : char *
465 : br_find_sbin_dir (const char *default_sbin_dir)
466 : {
467 : char *prefix, *dir;
468 :
469 0 : prefix = br_find_prefix ((const char *) NULL);
470 0 : if (prefix == (char *) NULL) {
471 : /* BinReloc not initialized. */
472 0 : if (default_sbin_dir != (const char *) NULL)
473 0 : return strdup (default_sbin_dir);
474 : else
475 0 : return (char *) NULL;
476 : }
477 :
478 0 : dir = br_build_path (prefix, "sbin");
479 0 : free (prefix);
480 0 : return dir;
481 0 : }
482 :
483 :
484 : /** Locate the application's data folder.
485 : *
486 : * The path is generated by the following pseudo-code evaluation:
487 : * \code
488 : * prefix + "/share"
489 : * \endcode
490 : *
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
496 : * returned.
497 : */
498 : char *
499 : br_find_data_dir (const char *default_data_dir)
500 : {
501 : char *prefix, *dir;
502 :
503 0 : prefix = br_find_prefix ((const char *) NULL);
504 0 : if (prefix == (char *) NULL) {
505 : /* BinReloc not initialized. */
506 0 : if (default_data_dir != (const char *) NULL)
507 0 : return strdup (default_data_dir);
508 : else
509 0 : return (char *) NULL;
510 : }
511 :
512 0 : dir = br_build_path (prefix, "share");
513 0 : free (prefix);
514 0 : return dir;
515 0 : }
516 :
517 :
518 : /** Locate the application's localization folder.
519 : *
520 : * The path is generated by the following pseudo-code evaluation:
521 : * \code
522 : * prefix + "/share/locale"
523 : * \endcode
524 : *
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.
530 : */
531 : char *
532 : br_find_locale_dir (const char *default_locale_dir)
533 : {
534 : char *data_dir, *dir;
535 :
536 0 : data_dir = br_find_data_dir ((const char *) NULL);
537 0 : if (data_dir == (char *) NULL) {
538 : /* BinReloc not initialized. */
539 0 : if (default_locale_dir != (const char *) NULL)
540 0 : return strdup (default_locale_dir);
541 : else
542 0 : return (char *) NULL;
543 : }
544 :
545 0 : dir = br_build_path (data_dir, "locale");
546 0 : free (data_dir);
547 0 : return dir;
548 0 : }
549 :
550 :
551 : /** Locate the application's library folder.
552 : *
553 : * The path is generated by the following pseudo-code evaluation:
554 : * \code
555 : * prefix + "/lib"
556 : * \endcode
557 : *
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.
563 : */
564 : char *
565 : br_find_lib_dir (const char *default_lib_dir)
566 : {
567 : char *prefix, *dir;
568 :
569 0 : prefix = br_find_prefix ((const char *) NULL);
570 0 : if (prefix == (char *) NULL) {
571 : /* BinReloc not initialized. */
572 0 : if (default_lib_dir != (const char *) NULL)
573 0 : return strdup (default_lib_dir);
574 : else
575 0 : return (char *) NULL;
576 : }
577 :
578 0 : dir = br_build_path (prefix, "lib");
579 0 : free (prefix);
580 0 : return dir;
581 0 : }
582 :
583 :
584 : /** Locate the application's libexec folder.
585 : *
586 : * The path is generated by the following pseudo-code evaluation:
587 : * \code
588 : * prefix + "/libexec"
589 : * \endcode
590 : *
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.
596 : */
597 : char *
598 : br_find_libexec_dir (const char *default_libexec_dir)
599 : {
600 : char *prefix, *dir;
601 :
602 0 : prefix = br_find_prefix ((const char *) NULL);
603 0 : if (prefix == (char *) NULL) {
604 : /* BinReloc not initialized. */
605 0 : if (default_libexec_dir != (const char *) NULL)
606 0 : return strdup (default_libexec_dir);
607 : else
608 0 : return (char *) NULL;
609 : }
610 :
611 0 : dir = br_build_path (prefix, "libexec");
612 0 : free (prefix);
613 0 : return dir;
614 0 : }
615 :
616 :
617 : /** Locate the application's configuration files folder.
618 : *
619 : * The path is generated by the following pseudo-code evaluation:
620 : * \code
621 : * prefix + "/etc"
622 : * \endcode
623 : *
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.
629 : */
630 : char *
631 : br_find_etc_dir (const char *default_etc_dir)
632 : {
633 : char *prefix, *dir;
634 :
635 0 : prefix = br_find_prefix ((const char *) NULL);
636 0 : if (prefix == (char *) NULL) {
637 : /* BinReloc not initialized. */
638 0 : if (default_etc_dir != (const char *) NULL)
639 0 : return strdup (default_etc_dir);
640 : else
641 0 : return (char *) NULL;
642 : }
643 :
644 0 : dir = br_build_path (prefix, "etc");
645 0 : free (prefix);
646 0 : return dir;
647 0 : }
648 :
649 :
650 : /***********************
651 : * Utility functions
652 : ***********************/
653 :
654 : /** Concatenate str1 and str2 to a newly allocated string.
655 : *
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.
659 : */
660 : char *
661 : br_strcat (const char *str1, const char *str2)
662 : {
663 : char *result;
664 : size_t len1, len2;
665 :
666 0 : if (str1 == NULL)
667 0 : str1 = "";
668 0 : if (str2 == NULL)
669 0 : str2 = "";
670 :
671 0 : len1 = strlen (str1);
672 0 : len2 = strlen (str2);
673 :
674 0 : result = (char *) malloc (len1 + len2 + 1);
675 0 : memcpy (result, str1, len1);
676 0 : memcpy (result + len1, str2, len2);
677 0 : result[len1 + len2] = '\0';
678 :
679 0 : return result;
680 : }
681 :
682 :
683 : char *
684 : br_build_path (const char *dir, const char *file)
685 : {
686 : char *dir2, *result;
687 : size_t len;
688 : int must_free = 0;
689 :
690 0 : len = strlen (dir);
691 0 : if (len > 0 && dir[len - 1] != '/') {
692 0 : dir2 = br_strcat (dir, "/");
693 : must_free = 1;
694 0 : } else
695 : dir2 = (char *) dir;
696 :
697 0 : result = br_strcat (dir2, file);
698 0 : if (must_free)
699 0 : free (dir2);
700 0 : return result;
701 : }
702 :
703 :
704 : /* Emulates glibc's strndup() */
705 : static char *
706 : br_strndup (const char *str, size_t size)
707 : {
708 : char *result = (char *) NULL;
709 : size_t len;
710 :
711 0 : if (str == (const char *) NULL)
712 0 : return (char *) NULL;
713 :
714 0 : len = strlen (str);
715 0 : if (len == 0)
716 0 : return strdup ("");
717 0 : if (size > len)
718 0 : size = len;
719 :
720 0 : result = (char *) malloc (len + 1);
721 0 : memcpy (result, str, size);
722 0 : result[size] = '\0';
723 0 : return result;
724 0 : }
725 :
726 :
727 : /** Extracts the directory component of a path.
728 : *
729 : * Similar to g_dirname() or the dirname commandline application.
730 : *
731 : * Example:
732 : * \code
733 : * br_dirname ("/usr/local/foobar"); --> Returns: "/usr/local"
734 : * \endcode
735 : *
736 : * @param path A path.
737 : * @returns A directory name. This string should be freed when no longer needed.
738 : */
739 : char *
740 : br_dirname (const char *path)
741 : {
742 : char *end, *result;
743 :
744 0 : if (path == (const char *) NULL)
745 0 : return (char *) NULL;
746 :
747 0 : end = strrchr (path, '/');
748 0 : if (end == (const char *) NULL)
749 0 : return strdup (".");
750 :
751 0 : while (end > path && *end == '/')
752 0 : end--;
753 0 : result = br_strndup (path, end - path + 1);
754 0 : if (result[0] == 0) {
755 0 : free (result);
756 0 : return strdup ("/");
757 : } else
758 0 : return result;
759 0 : }
760 :
761 :
762 : #ifdef __cplusplus
763 : }
764 : #endif /* __cplusplus */
765 :
766 : #endif /* __BINRELOC_C__ */
|