diff --git a/src/gallium/drivers/zink/zink_screen.c b/src/gallium/drivers/zink/zink_screen.c index 13f3667c838..0046be0c598 100644 --- a/src/gallium/drivers/zink/zink_screen.c +++ b/src/gallium/drivers/zink/zink_screen.c @@ -3370,7 +3370,16 @@ zink_internal_create_screen(const struct pipe_screen_config *config, int64_t dev u_trace_state_init(); - screen->loader_lib = util_dl_open(VK_LIBNAME); + /* Check for MESA_VULKAN_LIBRARY environment variable first (useful on macOS where + * SIP strips DYLD_LIBRARY_PATH from hardened processes like Java) */ + const char *vk_lib_env = getenv("MESA_VULKAN_LIBRARY"); + if (vk_lib_env) { + screen->loader_lib = util_dl_open(vk_lib_env); + if (screen->loader_lib) + fprintf(stderr, "ZINK: loaded vulkan from MESA_VULKAN_LIBRARY=%s\n", vk_lib_env); + } + if (!screen->loader_lib) + screen->loader_lib = util_dl_open(VK_LIBNAME); if (!screen->loader_lib) { if (!screen->driver_name_is_inferred) mesa_loge("ZINK: failed to load "VK_LIBNAME); diff --git a/src/glwrapper/libgl_interpose.c b/src/glwrapper/libgl_interpose.c index 6cb24a7dbe8..befd6deea65 100644 --- a/src/glwrapper/libgl_interpose.c +++ b/src/glwrapper/libgl_interpose.c @@ -1,9 +1,18 @@ /* * Mesa libGL wrapper for macOS EGL using dlsym interposition + * + * This library intercepts dlsym calls to redirect GL function lookups + * to Mesa's EGL implementation. It finds libEGL.dylib relative to itself, + * making the install location-independent. + * + * It also sets MESA_EGL_LIBRARY and MESA_VULKAN_LIBRARY environment variables + * so that GLFW and Zink can find the libraries when DYLD_LIBRARY_PATH is + * stripped by SIP on macOS. */ #include #include +#include #include #include #include @@ -16,23 +25,75 @@ static void *egl_handle = NULL; static PFNEGLGETPROCADDRESSPROC mesa_eglGetProcAddress = NULL; static int initialized = 0; +static int env_vars_set = 0; +static char lib_dir[1024] = {0}; +static char egl_lib_path[1024] = {0}; + +/* Determine the library directory and EGL path */ +static void determine_paths(void) { + if (lib_dir[0] != '\0') return; + + Dl_info info; + if (dladdr((void*)determine_paths, &info) && info.dli_fname) { + const char *last_slash = strrchr(info.dli_fname, '/'); + if (last_slash) { + size_t dir_len = last_slash - info.dli_fname; + snprintf(lib_dir, sizeof(lib_dir), "%.*s", (int)dir_len, info.dli_fname); + snprintf(egl_lib_path, sizeof(egl_lib_path), "%s/libEGL.dylib", lib_dir); + } else { + snprintf(egl_lib_path, sizeof(egl_lib_path), "libEGL.dylib"); + } + } else { + snprintf(egl_lib_path, sizeof(egl_lib_path), "libEGL.dylib"); + } +} + +/* Set environment variables for library paths (called lazily) */ +static void set_env_vars(void) { + if (env_vars_set) return; + env_vars_set = 1; + + determine_paths(); + + /* Set EGL library path */ + setenv("MESA_EGL_LIBRARY", egl_lib_path, 1); + fprintf(stderr, "libGL interpose: Set MESA_EGL_LIBRARY=%s\n", egl_lib_path); + + /* Set Vulkan library path */ + if (lib_dir[0] != '\0') { + char vk_lib_path[1024]; + snprintf(vk_lib_path, sizeof(vk_lib_path), "%s/libvulkan.1.dylib", lib_dir); + setenv("MESA_VULKAN_LIBRARY", vk_lib_path, 1); + fprintf(stderr, "libGL interpose: Set MESA_VULKAN_LIBRARY=%s\n", vk_lib_path); + } +} static void ensure_initialized(void) { if (initialized) return; initialized = 1; - egl_handle = dlopen("/Users/lucamignatti/mesa-native/lib/libEGL.dylib", RTLD_NOW | RTLD_LOCAL); + determine_paths(); + set_env_vars(); + + egl_handle = dlopen(egl_lib_path, RTLD_NOW | RTLD_LOCAL); if (egl_handle) { - fprintf(stderr, "libGL interpose: Loaded Mesa EGL\n"); + fprintf(stderr, "libGL interpose: Loaded Mesa EGL from %s\n", egl_lib_path); mesa_eglGetProcAddress = (PFNEGLGETPROCADDRESSPROC)dlsym(egl_handle, "eglGetProcAddress"); if (mesa_eglGetProcAddress) { fprintf(stderr, "libGL interpose: Ready to forward GL calls\n"); } + } else { + fprintf(stderr, "libGL interpose: Failed to load %s: %s\n", egl_lib_path, dlerror()); } } /* Interpose dlsym to catch GL function lookups */ -void *my_dlsym(void *handle, const char *symbol) { +static void *my_dlsym(void *handle, const char *symbol) { + /* Set env vars early so other libraries can use them */ + if (!env_vars_set) { + set_env_vars(); + } + ensure_initialized(); /* If looking for a GL function, use eglGetProcAddress */ diff --git a/src/glwrapper/libgl_wrapper.c b/src/glwrapper/libgl_wrapper.c index ec87aaf2993..b081cfd6ee4 100644 --- a/src/glwrapper/libgl_wrapper.c +++ b/src/glwrapper/libgl_wrapper.c @@ -34,22 +34,29 @@ static PFNEGLGETPROCADDRESSPROC mesa_eglGetProcAddress = NULL; __attribute__((constructor)) static void init_egl_loader(void) { - const char *egl_paths[] = { - "/Users/lucamignatti/mesa-native/lib/libEGL.dylib", - "libEGL.dylib", - NULL - }; + /* Find libEGL.dylib relative to this library (they're in the same directory) */ + Dl_info info; + char egl_path[1024]; - for (int i = 0; egl_paths[i] != NULL; i++) { - egl_handle = dlopen(egl_paths[i], RTLD_NOW | RTLD_LOCAL); - if (egl_handle) { - fprintf(stderr, "libGL wrapper: Loaded EGL from %s\n", egl_paths[i]); - break; + if (dladdr((void*)init_egl_loader, &info) && info.dli_fname) { + /* Get directory of this library */ + const char *last_slash = strrchr(info.dli_fname, '/'); + if (last_slash) { + size_t dir_len = last_slash - info.dli_fname; + snprintf(egl_path, sizeof(egl_path), "%.*s/libEGL.dylib", (int)dir_len, info.dli_fname); + } else { + snprintf(egl_path, sizeof(egl_path), "libEGL.dylib"); } + } else { + /* Fallback to just the filename, let dyld search */ + snprintf(egl_path, sizeof(egl_path), "libEGL.dylib"); } - if (!egl_handle) { - fprintf(stderr, "libGL wrapper: Failed to load libEGL.dylib\n"); + egl_handle = dlopen(egl_path, RTLD_NOW | RTLD_LOCAL); + if (egl_handle) { + fprintf(stderr, "libGL wrapper: Loaded EGL from %s\n", egl_path); + } else { + fprintf(stderr, "libGL wrapper: Failed to load %s: %s\n", egl_path, dlerror()); return; } diff --git a/src/glwrapper/meson.build b/src/glwrapper/meson.build new file mode 100644 index 00000000000..44ebf8e80a2 --- /dev/null +++ b/src/glwrapper/meson.build @@ -0,0 +1,30 @@ +# Copyright © 2024 Luca Mignatti +# SPDX-License-Identifier: MIT + +# libgl_interpose.dylib - intercepts dlsym calls to redirect GL lookups to EGL +# This is macOS-specific and uses DYLD_INTERPOSE + +libgl_interpose = shared_library( + 'gl_interpose', + 'libgl_interpose.c', + include_directories : [inc_include], + dependencies : [dep_dl], + install : true, + name_prefix : 'lib', + name_suffix : 'dylib', +) + +# libGL.dylib - wrapper that provides GL function symbols forwarded to EGL +# This is for applications that load libGL and dlsym for GL functions + +libgl_wrapper = shared_library( + 'GL', + 'libgl_wrapper.c', + include_directories : [inc_include], + dependencies : [dep_dl], + # These functions are intentionally exported without prototypes + c_args : ['-Wno-missing-prototypes'], + install : true, + name_prefix : 'lib', + name_suffix : 'dylib', +) diff --git a/src/meson.build b/src/meson.build index f27dae33631..866c1c4948b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -153,6 +153,9 @@ endif if with_egl subdir('egl') endif +if host_machine.system() == 'darwin' and with_egl + subdir('glwrapper') +endif if with_gallium and with_gbm if with_glx == 'dri' or with_platform_x11 or with_platform_xcb subdir('gallium/targets/dril')