Commit110fbd1aRecorded20 Sep 2026Repositorysigil-app
Make dl-search.c a shared unit configured by defines
Message
sigil-audio needs the same resolver for miniaudio's runtime loads, and a program may link either package without the other, so the file is now parameterised: SIGIL_DL_SEARCH_OPEN names the resolver, SIGIL_DL_SEARCH_INTERPOSE says whether to define dlopen itself (this package does; sigil-audio exposes the named entry only), and the tag and verbose variable follow. package.sgl passes them; the probe calls plain dlopen under INTERPOSE=1 and the named resolver otherwise. The file is copied byte for byte into sigil-audio, which records the hash.
Changed
package.sgl | 12 +++++++++++-
src/c/dl-search.c | 53 +++++++++++++++++++++++++++++++++++++++++------------
test/dl-search-probe.c | 14 +++++++++++---
test/dl-search-test.sh | 2 +-
4 files changed, 64 insertions(+), 17 deletions(-)Diff
package.sglmodified
@@ -78,8 +78,18 @@
78
(define desktop-include-dirs 79
'("include" "vendor/glfw/include")) 80
+81
;; The dl-search.c configuration (the file is shared with sigil-audio, see+82
;; its header): this package defines dlopen itself for the whole+83
;; executable, so GLFW's loads go through the resolver unpatched.+84
(define dl-search-flags+85
'("-DSIGIL_DL_SEARCH_OPEN=sigil_desktop_dl_open"+86
"-DSIGIL_DL_SEARCH_SUMMARY=sigil_desktop_dlopen_search_summary"+87
"-DSIGIL_DL_SEARCH_TAG=\"sigil-desktop\""+88
"-DSIGIL_DL_SEARCH_VERBOSE_ENV=\"SIGIL_DESKTOP_VERBOSE\""+89
"-DSIGIL_DL_SEARCH_INTERPOSE=1"))+90
91
(define desktop-flags−82
'("-std=c99" "-D_GNU_SOURCE" "-Wno-unused-parameter"))+92
(append '("-std=c99" "-D_GNU_SOURCE" "-Wno-unused-parameter") dl-search-flags)) 93
94
;; GLFW's own warning set is -Wall; its sources are compiled as they 95
;; ship, with only the platform defines above (no local patches).src/c/dl-search.cmodified
@@ -9,10 +9,11 @@
9
* LD_LIBRARY_PATH, and the distribution's own packages are patched to 10
* dlopen absolute store paths, which a user's binary cannot be. 11
*−12
* This file defines dlopen in the executable. The executable's symbols come+12
* In sigil-desktop this file defines dlopen in the executable. The executable's symbols come 13
* first in the lookup order, so every dlopen in the process (GLFW's in 14
* posix_module.c, and any other loader compiled into the program) resolves−15
* here. It tries the name as given first, so on a system where the loader+15
* here; in sigil-audio it exposes a named resolver that miniaudio calls+16
* instead of dlopen. It tries the name as given first, so on a system where the loader 17
* already finds the library nothing changes. When that fails and the name 18
* is a bare soname (no slash), it retries the name under each entry of the 19
* table below, in order. The real dlopen is reached with@@ -23,7 +24,8 @@
24
* A NEEDED entry (a library linked with -l) is resolved by the loader 25
* before main runs and cannot be helped here; only dlopen is covered. 26
*−26
* SIGIL_DESKTOP_VERBOSE=1 prints, for every fallback that succeeds, the+27
* The verbose env var (SIGIL_DESKTOP_VERBOSE=1 in sigil-desktop,+28
* SIGIL_AUDIO_VERBOSE=1 in sigil-audio) prints, for every fallback that succeeds, the 29
* soname, the table entry and the path, and whether /gnu/store and 30
* /nix/store exist on this machine. 31
*@@ -31,6 +33,25 @@
33
* libSystem's dlopen, and on Windows LoadLibrary has its own search order. 34
*/ 35
+36
+37
/* ---- per-package configuration ---------------------------------------+38
* This file is shared by sigil-desktop and sigil-audio, byte for byte+39
* (each repo's test checks its copy against the other's recorded hash).+40
* The package that compiles it says, with -D flags:+41
* SIGIL_DL_SEARCH_OPEN the resolver's name, e.g. sigil_desktop_dl_open+42
* SIGIL_DL_SEARCH_SUMMARY the summary function's name+43
* SIGIL_DL_SEARCH_TAG the log prefix, e.g. "sigil-desktop"+44
* SIGIL_DL_SEARCH_VERBOSE_ENV the env var that turns the trace on+45
* SIGIL_DL_SEARCH_INTERPOSE 1 to also define dlopen itself (one package+46
* per executable may; sigil-desktop does),+47
* 0 to expose only the named resolver (a+48
* loader compiled in the same package calls+49
* it, e.g. miniaudio through a define) */+50
#if !defined(SIGIL_DL_SEARCH_OPEN) || !defined(SIGIL_DL_SEARCH_SUMMARY) || \+51
!defined(SIGIL_DL_SEARCH_TAG) || !defined(SIGIL_DL_SEARCH_VERBOSE_ENV) || \+52
!defined(SIGIL_DL_SEARCH_INTERPOSE)+53
#error "dl-search.c: define SIGIL_DL_SEARCH_OPEN, _SUMMARY, _TAG, _VERBOSE_ENV and _INTERPOSE (see the file header)"+54
#endif 55
#ifndef _GNU_SOURCE 56
#define _GNU_SOURCE 57
#endif@@ -105,12 +126,12 @@ static int dir_exists(const char *path)
126
127
static void trace_found(const search_entry *e, const char *name, const char *path) 128
{−108
if (!getenv("SIGIL_DESKTOP_VERBOSE")) return;+129
if (!getenv(SIGIL_DL_SEARCH_VERBOSE_ENV)) return; 130
const char *label = (e->kind == ENTRY_DIR) ? e->name 131
: (e->kind == ENTRY_HOME) ? "HOME" 132
: (e->kind == ENTRY_USER) ? "USER" 133
: e->name;−113
fprintf(stderr, "sigil-desktop: dlopen %s: found via %s%s as %s (/gnu/store %s, /nix/store %s)\n",+134
fprintf(stderr, SIGIL_DL_SEARCH_TAG ": dlopen %s: found via %s%s as %s (/gnu/store %s, /nix/store %s)\n", 135
name, label, e->suffix, path, 136
dir_exists("/gnu/store") ? "present" : "absent", 137
dir_exists("/nix/store") ? "present" : "absent");@@ -178,15 +199,15 @@ static void *try_entry(sigil_dlopen_fn real, const search_entry *e, const char *
199
return NULL; 200
} 201
−181
/* ---- the interposed dlopen ------------------------------------------- */+202
/* ---- the resolver ---------------------------------------------------- */ 203
−183
void *dlopen(const char *name, int flags)+204
void *SIGIL_DL_SEARCH_OPEN(const char *name, int flags) 205
{ 206
sigil_dlopen_fn real = real_dlopen(); 207
if (!real) { 208
/* No next dlopen: only a fully static link gets here; say so, since−188
* GLFW then reports the library as missing. */−189
fprintf(stderr, "sigil-desktop: dlopen(%s): no underlying dlopen (static link?)\n",+209
* the caller then reports the library as missing. */+210
fprintf(stderr, SIGIL_DL_SEARCH_TAG ": dlopen(%s): no underlying dlopen (static link?)\n", 211
name ? name : "(null)"); 212
return NULL; 213
}@@ -205,9 +226,17 @@ void *dlopen(const char *name, int flags)
226
return real(name, flags); 227
} 228
−208
/* Referenced from desktop.c so this object is always linked ahead of libc,−209
* whatever order the linker meets the archive members in. */−210
const char *sigil_desktop_dlopen_search_summary(void)+229
#if SIGIL_DL_SEARCH_INTERPOSE+230
/* The executable's dlopen: every dlopen in the process resolves here. */+231
void *dlopen(const char *name, int flags)+232
{+233
return SIGIL_DL_SEARCH_OPEN(name, flags);+234
}+235
#endif+236
+237
/* Referenced from the package's own code so this object is always linked+238
* ahead of libc, whatever order the linker meets the archive members in. */+239
const char *SIGIL_DL_SEARCH_SUMMARY(void) 240
{ 241
return "SIGIL_LIBRARY_PATH; $GUIX_ENVIRONMENT/lib; each $buildInputs and " 242
"$nativeBuildInputs root + /lib under IN_NIX_SHELL; $NIX_LD_LIBRARY_PATH; ~/.guix-home/profile/lib; "test/dl-search-probe.cmodified
@@ -1,5 +1,8 @@
1
/* dl-search-probe.c - links dl-search.c and reports which file a soname−2
* resolved to. Driven by test/dl-search-test.sh.+2
* resolved to. Driven by test/dl-search-test.sh, compiled with the same+3
* -D configuration as the package: with SIGIL_DL_SEARCH_INTERPOSE=1 it+4
* calls plain dlopen (proving the interposition), with 0 it calls the+5
* named resolver the package's loader calls. 6
* usage: dl-search-probe <soname> exit 0 and print the path on success */ 7
#ifndef _GNU_SOURCE 8
#define _GNU_SOURCE@@ -8,16 +11,21 @@
11
#include <link.h> 12
#include <stdio.h> 13
−11
const char *sigil_desktop_dlopen_search_summary(void);+14
void *SIGIL_DL_SEARCH_OPEN(const char *name, int flags);+15
const char *SIGIL_DL_SEARCH_SUMMARY(void); 16
17
int main(int argc, char **argv) 18
{ 19
if (argc != 2) { fprintf(stderr, "usage: %s <soname>\n", argv[0]); return 2; }+20
#if SIGIL_DL_SEARCH_INTERPOSE 21
void *h = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL);+22
#else+23
void *h = SIGIL_DL_SEARCH_OPEN(argv[1], RTLD_NOW | RTLD_LOCAL);+24
#endif 25
if (!h) { printf("UNRESOLVED %s: %s\n", argv[1], dlerror()); return 1; } 26
struct link_map *lm = NULL; 27
if (dlinfo(h, RTLD_DI_LINKMAP, &lm) == 0 && lm) printf("RESOLVED %s -> %s\n", argv[1], lm->l_name); 28
else printf("RESOLVED %s\n", argv[1]);−21
(void)sigil_desktop_dlopen_search_summary;+29
(void)SIGIL_DL_SEARCH_SUMMARY; 30
return 0; 31
}test/dl-search-test.shmodified
@@ -26,7 +26,7 @@ trap 'rm -rf "$T"' EXIT
26
FAKE=libsigildlsearchfake.so.7 27
printf 'int sigil_fake_answer(void) { return 42; }\n' > "$T/fake.c" 28
$CC -shared -fPIC -o "$T/$FAKE" "$T/fake.c" || { echo "SETUP-FAILED: cannot build the fake library"; exit 2; }−29
$CC -o "$T/probe" test/dl-search-probe.c src/c/dl-search.c -ldl || { echo "SETUP-FAILED: cannot build the probe"; exit 2; }+29
$CC -o "$T/probe" -DSIGIL_DL_SEARCH_OPEN=sigil_desktop_dl_open -DSIGIL_DL_SEARCH_SUMMARY=sigil_desktop_dlopen_search_summary -DSIGIL_DL_SEARCH_TAG=\"sigil-desktop\" -DSIGIL_DL_SEARCH_VERBOSE_ENV=\"SIGIL_DESKTOP_VERBOSE\" -DSIGIL_DL_SEARCH_INTERPOSE=1 test/dl-search-probe.c src/c/dl-search.c -ldl || { echo "SETUP-FAILED: cannot build the probe"; exit 2; } 30
H="$T/home"; mkdir -p "$H/.guix-home/profile/lib" "$H/.nix-profile/lib" "$T/nixstore/foo-1.0/lib" 31
cp "$T/$FAKE" "$H/.nix-profile/lib/" 32
fails=0