]> git.cworth.org Git - glaze/blob - libglaze.c
Add egl definitions and related buildsupport.
[glaze] / libglaze.c
1 /* Copyright © 2013, Intel Corporation
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to deal
5  * in the Software without restriction, including without limitation the rights
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  * copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19  * THE SOFTWARE.
20  */
21
22 #include "config.h"
23
24 #define _GNU_SOURCE
25 #include <dlfcn.h>
26 #include <link.h>
27 #include <execinfo.h>
28
29 #include "glaze.h"
30
31 #include <talloc.h>
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <errno.h>
40
41 /* For PATH_MAX */
42 #include <linux/limits.h>
43
44 #ifdef __GNUC__
45 #define unused __attribute__ ((unused))
46 #else
47 #define unused
48 #endif
49
50 void **wrapper_handles;
51 int num_wrapper_handles;
52
53 /* FIXME: Both count_chars and open_wrapper_handles are copied code
54  * from glaze-gl.c. It would be better to avoid this code
55  * duplication. */
56
57 /* Count the number of types 'chr' appears in 'str' */
58 static int
59 count_chars (const char *str, char chr)
60 {
61         int count = 0;
62         const char *s = str;
63
64         while (1) {
65                 s = strchr (s, chr);
66                 if (s == NULL)
67                         break;
68                 count++;
69                 s++;
70                 if (*s == '\0')
71                         break;
72         }
73
74         return count;
75 }
76
77 static void
78 open_wrapper_handles (void)
79 {
80         const char *path;
81         char *path_copy, *wrapper, *save;
82         int i;
83
84         if (wrapper_handles)
85                 return;
86
87         path = getenv ("GLAZE_WRAPPER");
88         if (path == NULL) {
89                 fprintf (stderr, "GLAZE_WRAPPER unset. Please set to path of Glaze-using wrapper library.\n");
90                 exit (1);
91         }
92
93         num_wrapper_handles = count_chars (path, ':') + 1;
94
95         wrapper_handles = malloc (num_wrapper_handles * sizeof (void*));
96         if (wrapper_handles == NULL) {
97                 fprintf (stderr, "Out of memory\n");
98                 exit (1);
99         }
100
101         /* Clear dlerror state. */
102         dlerror ();
103
104         path_copy = strdup (path);
105         if (path_copy == NULL) {
106                 fprintf (stderr, "Out of memory\n");
107                 exit (1);
108         }
109
110         for (i = 0, wrapper = strtok_r (path_copy, ":", &save);
111              i < num_wrapper_handles;
112              i++, wrapper = strtok_r (NULL, ":", &save))
113         {
114                 wrapper_handles[i] = dlopen (wrapper, RTLD_LAZY);
115                 if (wrapper_handles[i] == NULL) {
116                         const char *error = dlerror();
117                         fprintf (stderr, "Error: Failed to dlopen %s: %s\n",
118                                  wrapper, error);
119                         exit (1);
120                 }
121         }
122
123         free (path_copy);
124 }
125
126 static int
127 symbol_is_in_backtrace (void *symbol, void **bt, int num_bt)
128 {
129         Dl_info info;
130         int status;
131         int i;
132
133         for (i = 0; i < num_bt; i++) {
134                 status = dladdr (bt[i], &info);
135                 if (status == 0)
136                         continue;
137                 if (info.dli_saddr == symbol)
138                         return 1;
139         }
140
141         return 0;
142 }
143
144 /* FIXME: The hardcoded MAX_BT value here is uncool.
145  *
146  * If the number is too small, we could miss one of the wrapper
147  * functions in the backtrace which could lead to an infinite
148  * loop. For this bound, something on the order of the number of
149  * wrappers is likely sufficiently large.
150  *
151  * Meanwhile, if the number here is too large, we just waste a bunch
152  * of time grubbing through the backtrace of the application itself.
153  *
154  * The Right Thing to do here is to look at the back trace and ensure
155  * we have seen back to Glaze's libGL.so. If we see that, then we've
156  * looked far enough and should not have to look any further.
157  *
158  * Until then, 15 levels of backtrace out to be enough for anybody...
159  */
160 #define MAX_BT 15
161
162 void *
163 glaze_lookup (char *function)
164 {
165         static void *libgl_handle = NULL;
166         void *ret;
167         void *bt_buffer[MAX_BT];
168         int i, num_bt;
169
170         /* Before looking up the symbol in the underlying libGL, we
171          * first check whether any wrapper library has that symbol. Of
172          * course, we're already within at least one wrapper, and it
173          * wants to call into the next. So, for each wrapper, skip the
174          * looked up symbol if it already appears in the current
175          * backtrace. */
176
177         if (wrapper_handles == NULL)
178                 open_wrapper_handles ();
179
180         num_bt = backtrace (bt_buffer, MAX_BT);
181
182         for (i = 0; i < num_wrapper_handles; i++) {
183                 ret = dlsym (wrapper_handles[i], function);
184                 if (ret == NULL)
185                         continue;
186
187                 /* Check if this function appears in the backtrace. */
188                 if (symbol_is_in_backtrace (ret, bt_buffer, num_bt))
189                         continue;
190
191                 /* Otherwise, we've found the right thing to chain to. */
192                 return ret;
193         }
194
195         /* Did not find the symbol in any wrapper library, so defer to
196          * the real, underlying function in libGL.so */
197         if (libgl_handle == NULL) {
198                 const char *path;
199
200                 path = getenv ("GLAZE_LIBGL");
201                 if (path == NULL) {
202                         fprintf (stderr, "GLAZE_LIBGL unset. "
203                                  "Please set to path of libGL.so under glaze.\n"
204                                 );
205                         exit (1);
206                 }
207
208                 libgl_handle = dlopen (path, RTLD_LAZY | RTLD_GLOBAL);
209                 if (libgl_handle == NULL) {
210                         fprintf (stderr, "glaze_lookup: Error: Failed to dlopen %s\n", path);
211                         exit (1);
212                 }
213         }
214
215         ret = dlsym (libgl_handle, function);
216
217         if (ret == NULL) {
218                 fprintf (stderr, "Error: glaze_lookup failed to dlsym %s\n",
219                          function);
220                 exit (1);
221         }
222
223         return ret;
224 }
225
226 /* Terminate a string representing a filename at the final '/' to
227  * eliminate the final filename component, (leaving only the directory
228  * portions of the original path).
229  *
230  * Notes: A path containing no '/' character will not be modified.
231  *        A path consisting only of "/" will not be modified.
232  */
233 static void
234 chop_trailing_path_component (char *path)
235 {
236         char *slash;
237
238         slash = strrchr (path, '/');
239
240         if (slash == NULL)
241                 return;
242
243         if (slash == path)
244                 return;
245
246         *slash = '\0';
247 }
248
249 /* Find the absolute path of the currently executing binary.
250  *
251  * Returns: a string talloc'ed to 'ctx'
252  */
253 static char *
254 get_bin_name (void *ctx)
255 {
256         const char *link = "/proc/self/exe";
257         char *name;
258
259         /* Yes, PATH_MAX is cheesy. I would have preferred to have
260          * used lstat and read the resulting st_size, but everytime I
261          * did that with /proc/self/exe I got a value of 0, (whereas
262          * with a "real" symbolic link I make myself I get the length
263          * of the filename being linked to). Go figure. */
264         int name_len = PATH_MAX + 1;
265
266         name = talloc_size (ctx, name_len);
267         if (name == NULL) {
268                 fprintf (stderr, "Out of memory.\n");
269                 exit (1);
270         }
271
272         name_len = readlink (link, name, name_len - 1);
273         if (name_len < 0) {
274                 fprintf (stderr, "Failed to readlink %s: %s\n", link,
275                          strerror (errno));
276                 exit (1);
277         }
278
279         name[name_len] = '\0';
280
281         return name;
282 }
283
284 /* Does path exist? */
285 static int
286 exists (const char *path)
287 {
288         struct stat st;
289         int err;
290
291         err = stat (path, &st);
292
293         /* Failed to stat. It either doesn't exist, or might as well not. */
294         if (err == -1)
295                 return 0;
296
297         return 1;
298 }
299
300 /* Execute "program" in a pipe, reads its first line of output on
301  * stdout, and returns that as a string (discarding any further
302  * output).
303  *
304  * Returns NULL if the program failed to execute for any reason.
305  *
306  * NOTE: The caller should free() the returned string when done with
307  * it.
308  */
309 static char *
310 read_process_output_one_line (const char *program)
311 {
312         FILE *process;
313         int status;
314         char *line = NULL;
315         size_t len = 0;
316         ssize_t bytes_read;
317
318         process = popen (program, "r");
319         if (process == NULL)
320                 return NULL;
321
322         bytes_read = getline (&line, &len, process);
323
324         status = pclose (process);
325         if (! WIFEXITED (status))
326                 return NULL;
327
328         if (WEXITSTATUS (status))
329                 return NULL;
330
331         if (bytes_read == -1)
332                 return NULL;
333
334         if (bytes_read) {
335                 if (line[strlen(line)-1] == '\n')
336                         line[strlen(line)-1] = '\0';
337                 return line;
338         } else {
339                 return NULL;
340         }
341 }
342
343
344 /* Look for "wrapper" library next to currently executing binary.
345  *
346  * If "wrapper" is an absolute path, return it directly.
347  *
348  * Otherwise, ("wrapper" is relative), look for an existing file named
349  * "wrapper" in the following locations in order:
350  *
351  *      1. The current working directory
352  *
353  *      2. The same directory as the currently executing binary, (as
354  *         determined by /proc/self/exe).
355  *
356  * If either of those files exist, return its path.
357  *
358  * Otherwise, return the original, relative "wrapper".
359  */
360 static const char *
361 resolve_wrapper_path (void *ctx, const char *wrapper)
362 {
363         char *cwd_path, *bin_path, *lib_path;
364
365         /* Return absolute wrapper path immediately. */
366         if (*wrapper == '/')
367                 return wrapper;
368
369         /* Look for wrapper in current working directory. */
370         cwd_path = get_current_dir_name ();
371
372         lib_path = talloc_asprintf (ctx, "%s/%s", cwd_path, wrapper);
373
374         free (cwd_path);
375
376         if (exists (lib_path))
377                 return lib_path;
378
379         talloc_free (lib_path);
380
381         /* Look for wrapper next to current executable. */
382         bin_path = get_bin_name (ctx);
383
384         chop_trailing_path_component (bin_path);
385
386         lib_path = talloc_asprintf (ctx, "%s/%s", bin_path, wrapper);
387
388         talloc_free (bin_path);
389
390         if (exists (lib_path))
391                 return lib_path;
392
393         talloc_free (lib_path);
394
395         /* Allow relative wrapper path to remain as-is. */
396         return wrapper;
397 }
398
399 /* Return path to directory containing Glaze wrapper's libGL.so.1
400  * suitable for use in LD_PRELOAD or LD_LIBRARY_PATH. Note that the
401  * path returned may not be a full path to the directory but may end
402  * with "$LIB" which will be expanded by the Linux dynamic linker to
403  * an architecture specific string (such as "lib/i386-linux-gnu" or
404  * "lib/x86_64-linux-gnu"). */
405 static const char *
406 find_glaze_libgl_dir (void)
407 {
408         return CONFIG_LIBDIR "/glaze/$LIB";
409 }
410
411 void
412 glaze_execute (int argc, char *argv[], const char *wrapper)
413 {
414         void *ctx = talloc_new (NULL);
415         int i;
416
417         /* Set GLAZE_WRAPPER to absolute path of wrapper library */
418         if (wrapper == NULL || *wrapper == '\0') {
419                 fprintf (stderr, "Error: glaze_execute called with empty wrapper library.\n");
420                 return;
421         }
422
423         wrapper = resolve_wrapper_path (ctx, wrapper);
424
425         /* If GLAZE_WRAPPER is already set, append so that both
426          * wrappers get involved correctly. */
427         if (getenv ("GLAZE_WRAPPER")) {
428                 char *aggregate;
429
430                 aggregate = talloc_asprintf (ctx, "%s:%s",
431                                              getenv ("GLAZE_WRAPPER"),
432                                              wrapper);
433                 setenv ("GLAZE_WRAPPER", aggregate, 1);
434                 talloc_free (aggregate);
435         } else {
436                 setenv ("GLAZE_WRAPPER", wrapper, 1);
437         }
438
439         /* Ensure GLAZE_LIBGL is set. If not, set GLAZE_LIBGL_32_AUTO
440          * and GLAZE_LIBGL_64_AUTO
441          *
442          * Note that we must do this before setting LD_LIBRARY_PATH,
443          * since after that, of course we would find Glaze's wrapper
444          * libGL.so.1. */
445         if (getenv ("GLAZE_LIBGL") == NULL) {
446                 char *libgl_path;
447
448                 libgl_path = read_process_output_one_line ("glaze-find-libgl-32");
449                 if (libgl_path) {
450                         setenv ("GLAZE_LIBGL_32_AUTO", libgl_path, 1);
451                         free (libgl_path);
452                 }
453
454                 libgl_path = read_process_output_one_line ("glaze-find-libgl-64");
455                 if (libgl_path) {
456                         setenv ("GLAZE_LIBGL_64_AUTO", libgl_path, 1);
457                         free (libgl_path);
458                 }
459         }
460
461         /* Set LD_LIBRARY_PATH to include glaze's own libGL.so */
462         const char *glaze_libgl_dir, *ld_library_path;
463
464         glaze_libgl_dir = find_glaze_libgl_dir ();
465
466         ld_library_path = getenv ("LD_LIBRARY_PATH");
467
468         if (ld_library_path == NULL)
469                 ld_library_path = glaze_libgl_dir;
470         else
471                 ld_library_path = talloc_asprintf (ctx, "%s:%s",
472                                                    glaze_libgl_dir,
473                                                    ld_library_path);
474
475         setenv ("LD_LIBRARY_PATH", ld_library_path, 1);
476
477         talloc_free (ctx);
478
479         /* Execute program */
480         execvp (argv[0], argv);
481
482         /* If execvp returns, something went wrong. */
483         fprintf (stderr, "Error: Failed to exec:");
484         for (i = 0; i < argc; i++)
485                 fprintf (stderr, " %s", argv[i]);
486         fprintf (stderr, "\n");
487
488         return;
489 }
490
491 void
492 glaze_set_first_gl_call_callback (const char *function_name)
493 {
494         setenv ("GLAZE_FIRST_GL_CALL_CALLBACK", function_name, 1);
495 }