macOS: Add portable libGL wrapper and fix library path discovery
- Add glwrapper subdirectory to build system for libgl_interpose.dylib and libGL.dylib - Make libgl_interpose.c and libgl_wrapper.c use dladdr() to find libEGL relative to themselves - Add MESA_EGL_LIBRARY and MESA_VULKAN_LIBRARY env var support for bypassing SIP's stripping of DYLD_LIBRARY_PATH - Zink now checks MESA_VULKAN_LIBRARY before default VK_LIBNAME This enables Mesa to work on macOS when DYLD_LIBRARY_PATH is stripped by System Integrity Protection, as happens with Java processes.
This commit is contained in:
@@ -3370,6 +3370,15 @@ zink_internal_create_screen(const struct pipe_screen_config *config, int64_t dev
|
||||
|
||||
u_trace_state_init();
|
||||
|
||||
/* 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)
|
||||
|
||||
@@ -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 <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <EGL/egl.h>
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 (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");
|
||||
}
|
||||
|
||||
egl_handle = dlopen(egl_path, RTLD_NOW | RTLD_LOCAL);
|
||||
if (egl_handle) {
|
||||
fprintf(stderr, "libGL wrapper: Loaded EGL from %s\n", egl_paths[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!egl_handle) {
|
||||
fprintf(stderr, "libGL wrapper: Failed to load libEGL.dylib\n");
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
30
src/glwrapper/meson.build
Normal file
30
src/glwrapper/meson.build
Normal file
@@ -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',
|
||||
)
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user