Skip to content

Commit 919e1d9

Browse files
committed
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.
1 parent 377cb4a commit 919e1d9

5 files changed

Lines changed: 126 additions & 16 deletions

File tree

src/gallium/drivers/zink/zink_screen.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3413,7 +3413,16 @@ zink_internal_create_screen(const struct pipe_screen_config *config, int64_t dev
34133413

34143414
u_trace_state_init();
34153415

3416-
screen->loader_lib = util_dl_open(VK_LIBNAME);
3416+
/* Check for MESA_VULKAN_LIBRARY environment variable first (useful on macOS where
3417+
* SIP strips DYLD_LIBRARY_PATH from hardened processes like Java) */
3418+
const char *vk_lib_env = getenv("MESA_VULKAN_LIBRARY");
3419+
if (vk_lib_env) {
3420+
screen->loader_lib = util_dl_open(vk_lib_env);
3421+
if (screen->loader_lib)
3422+
fprintf(stderr, "ZINK: loaded vulkan from MESA_VULKAN_LIBRARY=%s\n", vk_lib_env);
3423+
}
3424+
if (!screen->loader_lib)
3425+
screen->loader_lib = util_dl_open(VK_LIBNAME);
34173426
if (!screen->loader_lib) {
34183427
if (!screen->driver_name_is_inferred)
34193428
mesa_loge("ZINK: failed to load "VK_LIBNAME);

src/glwrapper/libgl_interpose.c

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
/*
22
* Mesa libGL wrapper for macOS EGL using dlsym interposition
3+
*
4+
* This library intercepts dlsym calls to redirect GL function lookups
5+
* to Mesa's EGL implementation. It finds libEGL.dylib relative to itself,
6+
* making the install location-independent.
7+
*
8+
* It also sets MESA_EGL_LIBRARY and MESA_VULKAN_LIBRARY environment variables
9+
* so that GLFW and Zink can find the libraries when DYLD_LIBRARY_PATH is
10+
* stripped by SIP on macOS.
311
*/
412

513
#include <dlfcn.h>
614
#include <stdio.h>
15+
#include <stdlib.h>
716
#include <string.h>
817
#include <stddef.h>
918
#include <EGL/egl.h>
@@ -16,23 +25,75 @@
1625
static void *egl_handle = NULL;
1726
static PFNEGLGETPROCADDRESSPROC mesa_eglGetProcAddress = NULL;
1827
static int initialized = 0;
28+
static int env_vars_set = 0;
29+
static char lib_dir[1024] = {0};
30+
static char egl_lib_path[1024] = {0};
31+
32+
/* Determine the library directory and EGL path */
33+
static void determine_paths(void) {
34+
if (lib_dir[0] != '\0') return;
35+
36+
Dl_info info;
37+
if (dladdr((void*)determine_paths, &info) && info.dli_fname) {
38+
const char *last_slash = strrchr(info.dli_fname, '/');
39+
if (last_slash) {
40+
size_t dir_len = last_slash - info.dli_fname;
41+
snprintf(lib_dir, sizeof(lib_dir), "%.*s", (int)dir_len, info.dli_fname);
42+
snprintf(egl_lib_path, sizeof(egl_lib_path), "%s/libEGL.dylib", lib_dir);
43+
} else {
44+
snprintf(egl_lib_path, sizeof(egl_lib_path), "libEGL.dylib");
45+
}
46+
} else {
47+
snprintf(egl_lib_path, sizeof(egl_lib_path), "libEGL.dylib");
48+
}
49+
}
50+
51+
/* Set environment variables for library paths (called lazily) */
52+
static void set_env_vars(void) {
53+
if (env_vars_set) return;
54+
env_vars_set = 1;
55+
56+
determine_paths();
57+
58+
/* Set EGL library path */
59+
setenv("MESA_EGL_LIBRARY", egl_lib_path, 1);
60+
fprintf(stderr, "libGL interpose: Set MESA_EGL_LIBRARY=%s\n", egl_lib_path);
61+
62+
/* Set Vulkan library path */
63+
if (lib_dir[0] != '\0') {
64+
char vk_lib_path[1024];
65+
snprintf(vk_lib_path, sizeof(vk_lib_path), "%s/libvulkan.1.dylib", lib_dir);
66+
setenv("MESA_VULKAN_LIBRARY", vk_lib_path, 1);
67+
fprintf(stderr, "libGL interpose: Set MESA_VULKAN_LIBRARY=%s\n", vk_lib_path);
68+
}
69+
}
1970

2071
static void ensure_initialized(void) {
2172
if (initialized) return;
2273
initialized = 1;
2374

24-
egl_handle = dlopen("/Users/lucamignatti/mesa-native/lib/libEGL.dylib", RTLD_NOW | RTLD_LOCAL);
75+
determine_paths();
76+
set_env_vars();
77+
78+
egl_handle = dlopen(egl_lib_path, RTLD_NOW | RTLD_LOCAL);
2579
if (egl_handle) {
26-
fprintf(stderr, "libGL interpose: Loaded Mesa EGL\n");
80+
fprintf(stderr, "libGL interpose: Loaded Mesa EGL from %s\n", egl_lib_path);
2781
mesa_eglGetProcAddress = (PFNEGLGETPROCADDRESSPROC)dlsym(egl_handle, "eglGetProcAddress");
2882
if (mesa_eglGetProcAddress) {
2983
fprintf(stderr, "libGL interpose: Ready to forward GL calls\n");
3084
}
85+
} else {
86+
fprintf(stderr, "libGL interpose: Failed to load %s: %s\n", egl_lib_path, dlerror());
3187
}
3288
}
3389

3490
/* Interpose dlsym to catch GL function lookups */
35-
void *my_dlsym(void *handle, const char *symbol) {
91+
static void *my_dlsym(void *handle, const char *symbol) {
92+
/* Set env vars early so other libraries can use them */
93+
if (!env_vars_set) {
94+
set_env_vars();
95+
}
96+
3697
ensure_initialized();
3798

3899
/* If looking for a GL function, use eglGetProcAddress */

src/glwrapper/libgl_wrapper.c

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,29 @@ static PFNEGLGETPROCADDRESSPROC mesa_eglGetProcAddress = NULL;
3434

3535
__attribute__((constructor))
3636
static void init_egl_loader(void) {
37-
const char *egl_paths[] = {
38-
"/Users/lucamignatti/mesa-native/lib/libEGL.dylib",
39-
"libEGL.dylib",
40-
NULL
41-
};
37+
/* Find libEGL.dylib relative to this library (they're in the same directory) */
38+
Dl_info info;
39+
char egl_path[1024];
4240

43-
for (int i = 0; egl_paths[i] != NULL; i++) {
44-
egl_handle = dlopen(egl_paths[i], RTLD_NOW | RTLD_LOCAL);
45-
if (egl_handle) {
46-
fprintf(stderr, "libGL wrapper: Loaded EGL from %s\n", egl_paths[i]);
47-
break;
41+
if (dladdr((void*)init_egl_loader, &info) && info.dli_fname) {
42+
/* Get directory of this library */
43+
const char *last_slash = strrchr(info.dli_fname, '/');
44+
if (last_slash) {
45+
size_t dir_len = last_slash - info.dli_fname;
46+
snprintf(egl_path, sizeof(egl_path), "%.*s/libEGL.dylib", (int)dir_len, info.dli_fname);
47+
} else {
48+
snprintf(egl_path, sizeof(egl_path), "libEGL.dylib");
4849
}
50+
} else {
51+
/* Fallback to just the filename, let dyld search */
52+
snprintf(egl_path, sizeof(egl_path), "libEGL.dylib");
4953
}
5054

51-
if (!egl_handle) {
52-
fprintf(stderr, "libGL wrapper: Failed to load libEGL.dylib\n");
55+
egl_handle = dlopen(egl_path, RTLD_NOW | RTLD_LOCAL);
56+
if (egl_handle) {
57+
fprintf(stderr, "libGL wrapper: Loaded EGL from %s\n", egl_path);
58+
} else {
59+
fprintf(stderr, "libGL wrapper: Failed to load %s: %s\n", egl_path, dlerror());
5360
return;
5461
}
5562

src/glwrapper/meson.build

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright © 2024 Luca Mignatti
2+
# SPDX-License-Identifier: MIT
3+
4+
# libgl_interpose.dylib - intercepts dlsym calls to redirect GL lookups to EGL
5+
# This is macOS-specific and uses DYLD_INTERPOSE
6+
7+
libgl_interpose = shared_library(
8+
'gl_interpose',
9+
'libgl_interpose.c',
10+
include_directories : [inc_include],
11+
dependencies : [dep_dl],
12+
install : true,
13+
name_prefix : 'lib',
14+
name_suffix : 'dylib',
15+
)
16+
17+
# libGL.dylib - wrapper that provides GL function symbols forwarded to EGL
18+
# This is for applications that load libGL and dlsym for GL functions
19+
20+
libgl_wrapper = shared_library(
21+
'GL',
22+
'libgl_wrapper.c',
23+
include_directories : [inc_include],
24+
dependencies : [dep_dl],
25+
# These functions are intentionally exported without prototypes
26+
c_args : ['-Wno-missing-prototypes'],
27+
install : true,
28+
name_prefix : 'lib',
29+
name_suffix : 'dylib',
30+
)

src/meson.build

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ endif
153153
if with_egl
154154
subdir('egl')
155155
endif
156+
if host_machine.system() == 'darwin' and with_egl
157+
subdir('glwrapper')
158+
endif
156159
if with_gallium
157160
if with_glx == 'dri' or with_platform_x11 or with_platform_xcb
158161
subdir('gallium/targets/dril')

0 commit comments

Comments
 (0)