1 /* Copyright © 2013, Intel Corporation
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:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
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
36 #include <sys/types.h>
42 #include <linux/limits.h>
45 #define unused __attribute__ ((unused))
50 void **wrapper_handles;
51 int num_wrapper_handles;
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
57 /* Count the number of types 'chr' appears in 'str' */
59 count_chars (const char *str, char chr)
78 open_wrapper_handles (void)
81 char *path_copy, *wrapper, *save;
87 path = getenv ("GLAZE_WRAPPER");
89 fprintf (stderr, "GLAZE_WRAPPER unset. Please set to path of Glaze-using wrapper library.\n");
93 num_wrapper_handles = count_chars (path, ':') + 1;
95 wrapper_handles = malloc (num_wrapper_handles * sizeof (void*));
96 if (wrapper_handles == NULL) {
97 fprintf (stderr, "Out of memory\n");
101 /* Clear dlerror state. */
104 path_copy = strdup (path);
105 if (path_copy == NULL) {
106 fprintf (stderr, "Out of memory\n");
110 for (i = 0, wrapper = strtok_r (path_copy, ":", &save);
111 i < num_wrapper_handles;
112 i++, wrapper = strtok_r (NULL, ":", &save))
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",
127 symbol_is_in_backtrace (void *symbol, void **bt, int num_bt)
133 for (i = 0; i < num_bt; i++) {
134 status = dladdr (bt[i], &info);
137 if (info.dli_saddr == symbol)
144 /* FIXME: The hardcoded MAX_BT value here is uncool.
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.
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.
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.
158 * Until then, 15 levels of backtrace out to be enough for anybody...
163 glaze_lookup (char *function)
165 static void *libgl_handle = NULL;
167 void *bt_buffer[MAX_BT];
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
177 if (wrapper_handles == NULL)
178 open_wrapper_handles ();
180 num_bt = backtrace (bt_buffer, MAX_BT);
182 for (i = 0; i < num_wrapper_handles; i++) {
183 ret = dlsym (wrapper_handles[i], function);
187 /* Check if this function appears in the backtrace. */
188 if (symbol_is_in_backtrace (ret, bt_buffer, num_bt))
191 /* Otherwise, we've found the right thing to chain to. */
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) {
200 path = getenv ("GLAZE_LIBGL");
202 fprintf (stderr, "GLAZE_LIBGL unset. "
203 "Please set to path of libGL.so under glaze.\n"
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);
215 ret = dlsym (libgl_handle, function);
218 fprintf (stderr, "Error: glaze_lookup failed to dlsym %s\n",
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).
230 * Notes: A path containing no '/' character will not be modified.
231 * A path consisting only of "/" will not be modified.
234 chop_trailing_path_component (char *path)
238 slash = strrchr (path, '/');
249 /* Find the absolute path of the currently executing binary.
251 * Returns: a string talloc'ed to 'ctx'
254 get_bin_name (void *ctx)
256 const char *link = "/proc/self/exe";
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;
266 name = talloc_size (ctx, name_len);
268 fprintf (stderr, "Out of memory.\n");
272 name_len = readlink (link, name, name_len - 1);
274 fprintf (stderr, "Failed to readlink %s: %s\n", link,
279 name[name_len] = '\0';
284 /* Does path exist? */
286 exists (const char *path)
291 err = stat (path, &st);
293 /* Failed to stat. It either doesn't exist, or might as well not. */
300 /* Execute "program" in a pipe, reads its first line of output on
301 * stdout, and returns that as a string (discarding any further
304 * Returns NULL if the program failed to execute for any reason.
306 * NOTE: The caller should free() the returned string when done with
310 read_process_output_one_line (const char *program)
318 process = popen (program, "r");
322 bytes_read = getline (&line, &len, process);
324 status = pclose (process);
325 if (! WIFEXITED (status))
328 if (WEXITSTATUS (status))
331 if (bytes_read == -1)
335 if (line[strlen(line)-1] == '\n')
336 line[strlen(line)-1] = '\0';
344 /* Look for "wrapper" library next to currently executing binary.
346 * If "wrapper" is an absolute path, return it directly.
348 * Otherwise, ("wrapper" is relative), look for an existing file named
349 * "wrapper" in the following locations in order:
351 * 1. The current working directory
353 * 2. The same directory as the currently executing binary, (as
354 * determined by /proc/self/exe).
356 * If either of those files exist, return its path.
358 * Otherwise, return the original, relative "wrapper".
361 resolve_wrapper_path (void *ctx, const char *wrapper)
363 char *cwd_path, *bin_path, *lib_path;
365 /* Return absolute wrapper path immediately. */
369 /* Look for wrapper in current working directory. */
370 cwd_path = get_current_dir_name ();
372 lib_path = talloc_asprintf (ctx, "%s/%s", cwd_path, wrapper);
376 if (exists (lib_path))
379 talloc_free (lib_path);
381 /* Look for wrapper next to current executable. */
382 bin_path = get_bin_name (ctx);
384 chop_trailing_path_component (bin_path);
386 lib_path = talloc_asprintf (ctx, "%s/%s", bin_path, wrapper);
388 talloc_free (bin_path);
390 if (exists (lib_path))
393 talloc_free (lib_path);
395 /* Allow relative wrapper path to remain as-is. */
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"). */
406 find_glaze_libgl_dir (void)
408 return CONFIG_LIBDIR "/glaze/$LIB";
412 glaze_execute (int argc, char *argv[], const char *wrapper)
414 void *ctx = talloc_new (NULL);
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");
423 wrapper = resolve_wrapper_path (ctx, wrapper);
425 /* If GLAZE_WRAPPER is already set, append so that both
426 * wrappers get involved correctly. */
427 if (getenv ("GLAZE_WRAPPER")) {
430 aggregate = talloc_asprintf (ctx, "%s:%s",
431 getenv ("GLAZE_WRAPPER"),
433 setenv ("GLAZE_WRAPPER", aggregate, 1);
434 talloc_free (aggregate);
436 setenv ("GLAZE_WRAPPER", wrapper, 1);
439 /* Ensure GLAZE_LIBGL is set. If not, set GLAZE_LIBGL_32_AUTO
440 * and GLAZE_LIBGL_64_AUTO
442 * Note that we must do this before setting LD_LIBRARY_PATH,
443 * since after that, of course we would find Glaze's wrapper
445 if (getenv ("GLAZE_LIBGL") == NULL) {
448 libgl_path = read_process_output_one_line ("glaze-find-libgl-32");
450 setenv ("GLAZE_LIBGL_32_AUTO", libgl_path, 1);
454 libgl_path = read_process_output_one_line ("glaze-find-libgl-64");
456 setenv ("GLAZE_LIBGL_64_AUTO", libgl_path, 1);
461 /* Set LD_LIBRARY_PATH to include glaze's own libGL.so */
462 const char *glaze_libgl_dir, *ld_library_path;
464 glaze_libgl_dir = find_glaze_libgl_dir ();
466 ld_library_path = getenv ("LD_LIBRARY_PATH");
468 if (ld_library_path == NULL)
469 ld_library_path = glaze_libgl_dir;
471 ld_library_path = talloc_asprintf (ctx, "%s:%s",
475 setenv ("LD_LIBRARY_PATH", ld_library_path, 1);
479 /* Execute program */
480 execvp (argv[0], argv);
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");
492 glaze_set_first_gl_call_callback (const char *function_name)
494 setenv ("GLAZE_FIRST_GL_CALL_CALLBACK", function_name, 1);