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