1 /*
2 * BinReloc - a library for creating relocatable executables
3 * Written by: Mike Hearn <mike@theoretic.com>
4 * Hongli Lai <h.lai@chello.nl>
5 * http://autopackage.org/
6 *
7 * This source code is public domain. You can relicense this code
8 * under whatever license you want.
9 *
10 * NOTE: if you're using C++ and are getting "undefined reference
11 * to br_*", try renaming prefix.c to prefix.cpp
12 */
14 /* WARNING, BEFORE YOU MODIFY PREFIX.C:
15 *
16 * If you make changes to any of the functions in prefix.c, you MUST
17 * change the BR_NAMESPACE macro (in prefix.h).
18 * This way you can avoid symbol table conflicts with other libraries
19 * that also happen to use BinReloc.
20 *
21 * Example:
22 * #define BR_NAMESPACE(funcName) foobar_ ## funcName
23 * --> expands br_locate to foobar_br_locate
24 */
26 #ifndef _PREFIX_C_
27 #define _PREFIX_C_
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
34 /* PLEASE NOTE: We use GThreads now for portability */
35 /* @see http://developer.gnome.org/doc/API/2.0/glib/glib-Threads.html */
36 #ifndef BR_THREADS
37 /* Change 1 to 0 if you don't want thread support */
38 #define BR_THREADS 1
39 #include <glib.h> //for GThreads
40 #endif /* BR_THREADS */
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <limits.h>
45 #include <string.h>
46 #include "prefix.h"
48 #ifdef __cplusplus
49 extern "C" {
50 #endif /* __cplusplus */
53 #undef NULL
54 #define NULL ((void *) 0)
56 #ifdef __GNUC__
57 #define br_return_val_if_fail(expr,val) if (!(expr)) {fprintf (stderr, "** BinReloc (%s): assertion %s failed\n", __PRETTY_FUNCTION__, #expr); return val;}
58 #else
59 #define br_return_val_if_fail(expr,val) if (!(expr)) return val
60 #endif /* __GNUC__ */
63 #ifdef ENABLE_BINRELOC
65 #include <sys/types.h>
66 #include <sys/stat.h>
67 #include <sys/param.h>
68 #include <unistd.h>
70 /**
71 * br_locate:
72 * symbol: A symbol that belongs to the app/library you want to locate.
73 * Returns: A newly allocated string containing the full path of the
74 * app/library that func belongs to, or NULL on error. This
75 * string should be freed when not when no longer needed.
76 *
77 * Finds out to which application or library symbol belongs, then locate
78 * the full path of that application or library.
79 * Note that symbol cannot be a pointer to a function. That will not work.
80 *
81 * Example:
82 * --> main.c
83 * #include "prefix.h"
84 * #include "libfoo.h"
85 *
86 * int main (int argc, char *argv[]) {
87 * printf ("Full path of this app: %s\n", br_locate (&argc));
88 * libfoo_start ();
89 * return 0;
90 * }
91 *
92 * --> libfoo.c starts here
93 * #include "prefix.h"
94 *
95 * void libfoo_start () {
96 * --> "" is a symbol that belongs to libfoo (because it's called
97 * --> from libfoo_start()); that's why this works.
98 * printf ("libfoo is located in: %s\n", br_locate (""));
99 * }
100 */
101 char *
102 br_locate (void *symbol)
103 {
104 char line[5000];
105 FILE *f;
106 char *path;
108 br_return_val_if_fail (symbol != NULL, NULL);
110 f = fopen ("/proc/self/maps", "r");
111 if (!f)
112 return NULL;
114 while (!feof (f))
115 {
116 unsigned long start, end;
118 if (!fgets (line, sizeof (line), f))
119 continue;
120 if (!strstr (line, " r-xp ") || !strchr (line, '/'))
121 continue;
123 sscanf (line, "%lx-%lx ", &start, &end);
124 if (symbol >= (void *) start && symbol < (void *) end)
125 {
126 char *tmp;
127 size_t len;
129 /* Extract the filename; it is always an absolute path */
130 path = strchr (line, '/');
132 /* Get rid of the newline */
133 tmp = strrchr (path, '\n');
134 if (tmp) *tmp = 0;
136 /* Get rid of "(deleted)" */
137 len = strlen (path);
138 if (len > 10 && strcmp (path + len - 10, " (deleted)") == 0)
139 {
140 tmp = path + len - 10;
141 *tmp = 0;
142 }
144 fclose(f);
145 return strdup (path);
146 }
147 }
149 fclose (f);
150 return NULL;
151 }
154 /**
155 * br_locate_prefix:
156 * symbol: A symbol that belongs to the app/library you want to locate.
157 * Returns: A prefix. This string should be freed when no longer needed.
158 *
159 * Locates the full path of the app/library that symbol belongs to, and return
160 * the prefix of that path, or NULL on error.
161 * Note that symbol cannot be a pointer to a function. That will not work.
162 *
163 * Example:
164 * --> This application is located in /usr/bin/foo
165 * br_locate_prefix (&argc); --> returns: "/usr"
166 */
167 char *
168 br_locate_prefix (void *symbol)
169 {
170 char *path, *prefix;
172 br_return_val_if_fail (symbol != NULL, NULL);
174 path = br_locate (symbol);
175 if (!path) return NULL;
177 prefix = br_extract_prefix (path);
178 free (path);
179 return prefix;
180 }
183 /**
184 * br_prepend_prefix:
185 * symbol: A symbol that belongs to the app/library you want to locate.
186 * path: The path that you want to prepend the prefix to.
187 * Returns: The new path, or NULL on error. This string should be freed when no
188 * longer needed.
189 *
190 * Gets the prefix of the app/library that symbol belongs to. Prepend that prefix to path.
191 * Note that symbol cannot be a pointer to a function. That will not work.
192 *
193 * Example:
194 * --> The application is /usr/bin/foo
195 * br_prepend_prefix (&argc, "/share/foo/data.png"); --> Returns "/usr/share/foo/data.png"
196 */
197 char *
198 br_prepend_prefix (void *symbol, char *path)
199 {
200 char *tmp, *newpath;
202 br_return_val_if_fail (symbol != NULL, NULL);
203 br_return_val_if_fail (path != NULL, NULL);
205 tmp = br_locate_prefix (symbol);
206 if (!tmp) return NULL;
208 if (strcmp (tmp, "/") == 0)
209 newpath = strdup (path);
210 else
211 newpath = br_strcat (tmp, path);
213 /* Get rid of compiler warning ("br_prepend_prefix never used") */
214 if (0) br_prepend_prefix (NULL, NULL);
216 free (tmp);
217 return newpath;
218 }
220 #endif /* ENABLE_BINRELOC */
223 /* Thread stuff for thread safetiness */
224 #if BR_THREADS
226 GPrivate* br_thread_key = (GPrivate *)NULL;
228 /*
229 We do not need local store init() or fini(), because
230 g_private_new (g_free) will take care of all of that
231 for us. Isn't GLib wonderful?
232 */
234 #else /* !BR_THREADS */
236 static char *br_last_value = (char*)NULL;
238 static void
239 br_free_last_value ()
240 {
241 if (br_last_value)
242 free (br_last_value);
243 }
245 #endif /* BR_THREADS */
248 /**
249 * br_thread_local_store:
250 * str: A dynamically allocated string.
251 * Returns: str. This return value must not be freed.
252 *
253 * Store str in a thread-local variable and return str. The next
254 * you run this function, that variable is freed too.
255 * This function is created so you don't have to worry about freeing
256 * strings.
257 *
258 * Example:
259 * char *foo;
260 * foo = thread_local_store (strdup ("hello")); --> foo == "hello"
261 * foo = thread_local_store (strdup ("world")); --> foo == "world"; "hello" is now freed.
262 */
263 const char *
264 br_thread_local_store (char *str)
265 {
266 #if BR_THREADS
267 if (!g_thread_supported ())
268 {
269 g_thread_init ((GThreadFunctions *)NULL);
270 br_thread_key = g_private_new (g_free);
271 }
273 char *specific = (char *) g_private_get (br_thread_key);
274 if (specific)
275 free (specific);
276 g_private_set (br_thread_key, str);
278 #else /* !BR_THREADS */
279 static int initialized = 0;
281 if (!initialized)
282 {
283 atexit (br_free_last_value);
284 initialized = 1;
285 }
287 if (br_last_value)
288 free (br_last_value);
289 br_last_value = str;
290 #endif /* BR_THREADS */
292 return (const char *) str;
293 }
296 /**
297 * br_strcat:
298 * str1: A string.
299 * str2: Another string.
300 * Returns: A newly-allocated string. This string should be freed when no longer needed.
301 *
302 * Concatenate str1 and str2 to a newly allocated string.
303 */
304 char *
305 br_strcat (const char *str1, const char *str2)
306 {
307 char *result;
308 size_t len1, len2;
310 if (!str1) str1 = "";
311 if (!str2) str2 = "";
313 len1 = strlen (str1);
314 len2 = strlen (str2);
316 result = (char *) malloc (len1 + len2 + 1);
317 memcpy (result, str1, len1);
318 memcpy (result + len1, str2, len2);
319 result[len1 + len2] = '\0';
321 return result;
322 }
325 /* Emulates glibc's strndup() */
326 static char *
327 br_strndup (char *str, size_t size)
328 {
329 char *result = (char*)NULL;
330 size_t len;
332 br_return_val_if_fail (str != (char*)NULL, (char*)NULL);
334 len = strlen (str);
335 if (!len) return strdup ("");
336 if (size > len) size = len;
338 result = (char *) calloc (sizeof (char), len + 1);
339 memcpy (result, str, size);
340 return result;
341 }
344 /**
345 * br_extract_dir:
346 * path: A path.
347 * Returns: A directory name. This string should be freed when no longer needed.
348 *
349 * Extracts the directory component of path. Similar to g_dirname() or the dirname
350 * commandline application.
351 *
352 * Example:
353 * br_extract_dir ("/usr/local/foobar"); --> Returns: "/usr/local"
354 */
355 char *
356 br_extract_dir (const char *path)
357 {
358 char *end, *result;
360 br_return_val_if_fail (path != (char*)NULL, (char*)NULL);
362 end = strrchr (path, '/');
363 if (!end) return strdup (".");
365 while (end > path && *end == '/')
366 end--;
367 result = br_strndup ((char *) path, end - path + 1);
368 if (!*result)
369 {
370 free (result);
371 return strdup ("/");
372 } else
373 return result;
374 }
377 /**
378 * br_extract_prefix:
379 * path: The full path of an executable or library.
380 * Returns: The prefix, or NULL on error. This string should be freed when no longer needed.
381 *
382 * Extracts the prefix from path. This function assumes that your executable
383 * or library is installed in an LSB-compatible directory structure.
384 *
385 * Example:
386 * br_extract_prefix ("/usr/bin/gnome-panel"); --> Returns "/usr"
387 * br_extract_prefix ("/usr/local/lib/libfoo.so"); --> Returns "/usr/local"
388 * br_extract_prefix ("/usr/local/libfoo.so"); --> Returns "/usr"
389 */
390 char *
391 br_extract_prefix (const char *path)
392 {
393 char *end, *tmp, *result;
395 br_return_val_if_fail (path != (char*)NULL, (char*)NULL);
397 if (!*path) return strdup ("/");
398 end = strrchr (path, '/');
399 if (!end) return strdup (path);
401 tmp = br_strndup ((char *) path, end - path);
402 if (!*tmp)
403 {
404 free (tmp);
405 return strdup ("/");
406 }
407 end = strrchr (tmp, '/');
408 if (!end) return tmp;
410 result = br_strndup (tmp, end - tmp);
411 free (tmp);
413 if (!*result)
414 {
415 free (result);
416 result = strdup ("/");
417 }
419 return result;
420 }
423 #ifdef __cplusplus
424 }
425 #endif /* __cplusplus */
427 #endif /* _PREFIX_C */