* [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen
@ 2025-10-17 10:50 Vivian Wang
2025-11-09 13:10 ` Alyssa Ross
2025-11-12 17:14 ` Rich Felker
0 siblings, 2 replies; 7+ messages in thread
From: Vivian Wang @ 2025-10-17 10:50 UTC (permalink / raw)
To: musl; +Cc: matthewcroughan
Grab the return address using an arch-specific wrapper dlopen calling a
generic __dlopen (analogous to dlsym and __dlsym), and use it to find
the dso to use as needed_by for load_library in __dlopen. This way, when
a dso calls dlopen, the library is searched from *this* dso's rpath.
This feature is used by shared libraries that dlopen on demand other
shared libraries found in nonstandard paths.
This makes the behavior of DT_RUNPATH match glibc better. Also, since we
already use this behavior with libraries loaded with DT_NEEDED, adding
support for dlopen makes it more consistent.
By coincidence, both __dlsym and __dlopen take three arguments, the last
of which is the return address. Therefore all of the arch-specific
src/ldso/*/dlopen.s is just the corresponding dlsym.s with "dlsym"
replaced by "dlopen".
---
I am not subscribed to the mailing list, so please cc on reply. Thanks.
I have primarily tested this on aarch64. I'm not sure whether this
approach makes sense. Please advise.
One observation is that musl falls back to using rpath entries from the
entire needed_by chain, so this change in behavior shouldn't break
existing working use cases.
---
ldso/dynlink.c | 75 ++++++++++++++++++++++---------------------
src/internal/dynlink.h | 1 +
src/ldso/__dlopen.c | 10 ++++++
src/ldso/aarch64/dlopen.s | 6 ++++
src/ldso/arm/dlopen.s | 8 +++++
src/ldso/dlopen.c | 8 ++---
src/ldso/i386/dlopen.s | 11 +++++++
src/ldso/loongarch64/dlopen.s | 7 ++++
src/ldso/m68k/dlopen.s | 12 +++++++
src/ldso/microblaze/dlopen.s | 6 ++++
src/ldso/mips/dlopen.s | 17 ++++++++++
src/ldso/mips64/dlopen.s | 17 ++++++++++
src/ldso/mipsn32/dlopen.s | 17 ++++++++++
src/ldso/or1k/dlopen.s | 6 ++++
src/ldso/powerpc/dlopen.s | 8 +++++
src/ldso/powerpc64/dlopen.s | 11 +++++++
src/ldso/riscv32/dlopen.s | 6 ++++
src/ldso/riscv64/dlopen.s | 6 ++++
src/ldso/s390x/dlopen.s | 6 ++++
src/ldso/sh/dlopen.s | 11 +++++++
src/ldso/x32/dlopen.s | 7 ++++
src/ldso/x86_64/dlopen.s | 7 ++++
22 files changed, 221 insertions(+), 42 deletions(-)
diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 715948f4..996d505a 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -2096,9 +2096,42 @@ static void prepare_lazy(struct dso *p)
lazy_head = p;
}
-void *dlopen(const char *file, int mode)
+static void *addr2dso(size_t a)
+{
+ struct dso *p;
+ size_t i;
+ if (DL_FDPIC) for (p=head; p; p=p->next) {
+ i = count_syms(p);
+ if (a-(size_t)p->funcdescs < i*sizeof(*p->funcdescs))
+ return p;
+ }
+ for (p=head; p; p=p->next) {
+ if (DL_FDPIC && p->loadmap) {
+ for (i=0; i<p->loadmap->nsegs; i++) {
+ if (a-p->loadmap->segs[i].p_vaddr
+ < p->loadmap->segs[i].p_memsz)
+ return p;
+ }
+ } else {
+ Phdr *ph = p->phdr;
+ size_t phcnt = p->phnum;
+ size_t entsz = p->phentsize;
+ size_t base = (size_t)p->base;
+ for (; phcnt--; ph=(void *)((char *)ph+entsz)) {
+ if (ph->p_type != PT_LOAD) continue;
+ if (a-base-ph->p_vaddr < ph->p_memsz)
+ return p;
+ }
+ if (a-(size_t)p->map < p->map_len)
+ return 0;
+ }
+ }
+ return 0;
+}
+
+void *__dlopen(const char *file, int mode, void *ra)
{
- struct dso *volatile p, *orig_tail, *orig_syms_tail, *orig_lazy_head, *next;
+ struct dso *volatile p, *needed_by, *orig_tail, *orig_syms_tail, *orig_lazy_head, *next;
struct tls_module *orig_tls_tail;
size_t orig_tls_cnt, orig_tls_offset, orig_tls_align;
size_t i;
@@ -2108,6 +2141,9 @@ void *dlopen(const char *file, int mode)
if (!file) return head;
+ needed_by = addr2dso((size_t)ra);
+ if (!needed_by) needed_by = head;
+
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
pthread_rwlock_wrlock(&lock);
__inhibit_ptc();
@@ -2160,7 +2196,7 @@ void *dlopen(const char *file, int mode)
tail->next = 0;
p = 0;
goto end;
- } else p = load_library(file, head);
+ } else p = load_library(file, needed_by);
if (!p) {
error(noload ?
@@ -2231,39 +2267,6 @@ hidden int __dl_invalid_handle(void *h)
return 1;
}
-static void *addr2dso(size_t a)
-{
- struct dso *p;
- size_t i;
- if (DL_FDPIC) for (p=head; p; p=p->next) {
- i = count_syms(p);
- if (a-(size_t)p->funcdescs < i*sizeof(*p->funcdescs))
- return p;
- }
- for (p=head; p; p=p->next) {
- if (DL_FDPIC && p->loadmap) {
- for (i=0; i<p->loadmap->nsegs; i++) {
- if (a-p->loadmap->segs[i].p_vaddr
- < p->loadmap->segs[i].p_memsz)
- return p;
- }
- } else {
- Phdr *ph = p->phdr;
- size_t phcnt = p->phnum;
- size_t entsz = p->phentsize;
- size_t base = (size_t)p->base;
- for (; phcnt--; ph=(void *)((char *)ph+entsz)) {
- if (ph->p_type != PT_LOAD) continue;
- if (a-base-ph->p_vaddr < ph->p_memsz)
- return p;
- }
- if (a-(size_t)p->map < p->map_len)
- return 0;
- }
- }
- return 0;
-}
-
static void *do_dlsym(struct dso *p, const char *s, void *ra)
{
int use_deps = 0;
diff --git a/src/internal/dynlink.h b/src/internal/dynlink.h
index 40c743e2..ce718927 100644
--- a/src/internal/dynlink.h
+++ b/src/internal/dynlink.h
@@ -106,6 +106,7 @@ struct fdpic_dummy_loadmap {
typedef void (*stage2_func)(unsigned char *, size_t *);
hidden void *__dlsym(void *restrict, const char *restrict, void *restrict);
+hidden void *__dlopen(const char *, int, void *restrict);
hidden void __dl_seterr(const char *, ...);
hidden int __dl_invalid_handle(void *);
diff --git a/src/ldso/__dlopen.c b/src/ldso/__dlopen.c
new file mode 100644
index 00000000..e8e381f3
--- /dev/null
+++ b/src/ldso/__dlopen.c
@@ -0,0 +1,10 @@
+#include <dlfcn.h>
+#include "dynlink.h"
+
+static void *stub_dlopen(const char *file, int mode, void *restrict ra)
+{
+ __dl_seterr("Dynamic loading not supported");
+ return 0;
+}
+
+weak_alias(stub_dlopen, __dlopen);
diff --git a/src/ldso/aarch64/dlopen.s b/src/ldso/aarch64/dlopen.s
new file mode 100644
index 00000000..a925317c
--- /dev/null
+++ b/src/ldso/aarch64/dlopen.s
@@ -0,0 +1,6 @@
+.global dlopen
+.hidden __dlopen
+.type dlopen,%function
+dlopen:
+ mov x2,x30
+ b __dlopen
diff --git a/src/ldso/arm/dlopen.s b/src/ldso/arm/dlopen.s
new file mode 100644
index 00000000..d6754b17
--- /dev/null
+++ b/src/ldso/arm/dlopen.s
@@ -0,0 +1,8 @@
+.syntax unified
+.text
+.global dlopen
+.hidden __dlopen
+.type dlopen,%function
+dlopen:
+ mov r2,lr
+ b __dlopen
diff --git a/src/ldso/dlopen.c b/src/ldso/dlopen.c
index 69372a22..888d8bdd 100644
--- a/src/ldso/dlopen.c
+++ b/src/ldso/dlopen.c
@@ -1,10 +1,6 @@
-#include <dlfcn.h>
#include "dynlink.h"
-static void *stub_dlopen(const char *file, int mode)
+void *dlopen(const char *file, int mode)
{
- __dl_seterr("Dynamic loading not supported");
- return 0;
+ return __dlopen(file, mode, 0);
}
-
-weak_alias(stub_dlopen, dlopen);
diff --git a/src/ldso/i386/dlopen.s b/src/ldso/i386/dlopen.s
new file mode 100644
index 00000000..ec98a52c
--- /dev/null
+++ b/src/ldso/i386/dlopen.s
@@ -0,0 +1,11 @@
+.text
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ push (%esp)
+ push 12(%esp)
+ push 12(%esp)
+ call __dlopen
+ add $12,%esp
+ ret
diff --git a/src/ldso/loongarch64/dlopen.s b/src/ldso/loongarch64/dlopen.s
new file mode 100644
index 00000000..aa014d37
--- /dev/null
+++ b/src/ldso/loongarch64/dlopen.s
@@ -0,0 +1,7 @@
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ move $a2, $ra
+ la.global $t0, __dlopen
+ jr $t0
diff --git a/src/ldso/m68k/dlopen.s b/src/ldso/m68k/dlopen.s
new file mode 100644
index 00000000..5d839ca2
--- /dev/null
+++ b/src/ldso/m68k/dlopen.s
@@ -0,0 +1,12 @@
+.text
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ move.l (%sp),-(%sp)
+ move.l 12(%sp),-(%sp)
+ move.l 12(%sp),-(%sp)
+ lea __dlopen-.-8,%a1
+ jsr (%pc,%a1)
+ add.l #12,%sp
+ rts
diff --git a/src/ldso/microblaze/dlopen.s b/src/ldso/microblaze/dlopen.s
new file mode 100644
index 00000000..940b88c9
--- /dev/null
+++ b/src/ldso/microblaze/dlopen.s
@@ -0,0 +1,6 @@
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ brid __dlopen
+ add r7, r15, r0
diff --git a/src/ldso/mips/dlopen.s b/src/ldso/mips/dlopen.s
new file mode 100644
index 00000000..791ee8d8
--- /dev/null
+++ b/src/ldso/mips/dlopen.s
@@ -0,0 +1,17 @@
+.set noreorder
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ lui $gp, %hi(_gp_disp)
+ addiu $gp, %lo(_gp_disp)
+ addu $gp, $gp, $25
+ move $6, $ra
+ lw $25, %call16(__dlopen)($gp)
+ addiu $sp, $sp, -16
+ sw $ra, 12($sp)
+ jalr $25
+ nop
+ lw $ra, 12($sp)
+ jr $ra
+ addiu $sp, $sp, 16
diff --git a/src/ldso/mips64/dlopen.s b/src/ldso/mips64/dlopen.s
new file mode 100644
index 00000000..95c2b11c
--- /dev/null
+++ b/src/ldso/mips64/dlopen.s
@@ -0,0 +1,17 @@
+.set noreorder
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ lui $3, %hi(%neg(%gp_rel(dlopen)))
+ daddiu $3, $3, %lo(%neg(%gp_rel(dlopen)))
+ daddu $3, $3, $25
+ move $6, $ra
+ ld $25, %got_disp(__dlopen)($3)
+ daddiu $sp, $sp, -32
+ sd $ra, 24($sp)
+ jalr $25
+ nop
+ ld $ra, 24($sp)
+ jr $ra
+ daddiu $sp, $sp, 32
diff --git a/src/ldso/mipsn32/dlopen.s b/src/ldso/mipsn32/dlopen.s
new file mode 100644
index 00000000..cccd92a7
--- /dev/null
+++ b/src/ldso/mipsn32/dlopen.s
@@ -0,0 +1,17 @@
+.set noreorder
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ lui $3, %hi(%neg(%gp_rel(dlopen)))
+ addiu $3, $3, %lo(%neg(%gp_rel(dlopen)))
+ addu $3, $3, $25
+ move $6, $ra
+ lw $25, %got_disp(__dlopen)($3)
+ addiu $sp, $sp, -32
+ sd $ra, 16($sp)
+ jalr $25
+ nop
+ ld $ra, 16($sp)
+ jr $ra
+ addiu $sp, $sp, 32
diff --git a/src/ldso/or1k/dlopen.s b/src/ldso/or1k/dlopen.s
new file mode 100644
index 00000000..2e7ae870
--- /dev/null
+++ b/src/ldso/or1k/dlopen.s
@@ -0,0 +1,6 @@
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ l.j __dlopen
+ l.ori r5, r9, 0
diff --git a/src/ldso/powerpc/dlopen.s b/src/ldso/powerpc/dlopen.s
new file mode 100644
index 00000000..49d52a5f
--- /dev/null
+++ b/src/ldso/powerpc/dlopen.s
@@ -0,0 +1,8 @@
+ .text
+ .global dlopen
+ .hidden __dlopen
+ .type dlopen,@function
+dlopen:
+ mflr 5 # The return address is arg3.
+ b __dlopen
+ .size dlopen, .-dlopen
diff --git a/src/ldso/powerpc64/dlopen.s b/src/ldso/powerpc64/dlopen.s
new file mode 100644
index 00000000..3bd43fba
--- /dev/null
+++ b/src/ldso/powerpc64/dlopen.s
@@ -0,0 +1,11 @@
+ .text
+ .global dlopen
+ .hidden __dlopen
+ .type dlopen,@function
+dlopen:
+ addis 2, 12, .TOC.-dlopen@ha
+ addi 2, 2, .TOC.-dlopen@l
+ .localentry dlopen,.-dlopen
+ mflr 5 # The return address is arg3.
+ b __dlopen
+ .size dlopen, .-dlopen
diff --git a/src/ldso/riscv32/dlopen.s b/src/ldso/riscv32/dlopen.s
new file mode 100644
index 00000000..308d4e8d
--- /dev/null
+++ b/src/ldso/riscv32/dlopen.s
@@ -0,0 +1,6 @@
+.global dlopen
+.hidden __dlopen
+.type dlopen, %function
+dlopen:
+ mv a2, ra
+ tail __dlopen
diff --git a/src/ldso/riscv64/dlopen.s b/src/ldso/riscv64/dlopen.s
new file mode 100644
index 00000000..308d4e8d
--- /dev/null
+++ b/src/ldso/riscv64/dlopen.s
@@ -0,0 +1,6 @@
+.global dlopen
+.hidden __dlopen
+.type dlopen, %function
+dlopen:
+ mv a2, ra
+ tail __dlopen
diff --git a/src/ldso/s390x/dlopen.s b/src/ldso/s390x/dlopen.s
new file mode 100644
index 00000000..ff2bb284
--- /dev/null
+++ b/src/ldso/s390x/dlopen.s
@@ -0,0 +1,6 @@
+ .global dlopen
+ .hidden __dlopen
+ .type dlopen,@function
+dlopen:
+ lgr %r4, %r14
+ jg __dlopen
diff --git a/src/ldso/sh/dlopen.s b/src/ldso/sh/dlopen.s
new file mode 100644
index 00000000..349c71d8
--- /dev/null
+++ b/src/ldso/sh/dlopen.s
@@ -0,0 +1,11 @@
+.text
+.global dlopen
+.hidden __dlopen
+.type dlopen, @function
+dlopen:
+ mov.l L1, r0
+1: braf r0
+ sts pr, r6
+
+.align 2
+L1: .long __dlopen@PLT-(1b+4-.)
diff --git a/src/ldso/x32/dlopen.s b/src/ldso/x32/dlopen.s
new file mode 100644
index 00000000..8196ab54
--- /dev/null
+++ b/src/ldso/x32/dlopen.s
@@ -0,0 +1,7 @@
+.text
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ mov (%rsp),%rdx
+ jmp __dlopen
diff --git a/src/ldso/x86_64/dlopen.s b/src/ldso/x86_64/dlopen.s
new file mode 100644
index 00000000..8196ab54
--- /dev/null
+++ b/src/ldso/x86_64/dlopen.s
@@ -0,0 +1,7 @@
+.text
+.global dlopen
+.hidden __dlopen
+.type dlopen,@function
+dlopen:
+ mov (%rsp),%rdx
+ jmp __dlopen
---
base-commit: 1b76ff0767d01df72f692806ee5adee13c67ef88
change-id: 20251016-dlopen-use-rpath-of-caller-dso-e78fd9e573d0
Best regards,
--
Vivian "dramforever" Wang
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen
2025-10-17 10:50 [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen Vivian Wang
@ 2025-11-09 13:10 ` Alyssa Ross
2025-11-12 17:14 ` Rich Felker
1 sibling, 0 replies; 7+ messages in thread
From: Alyssa Ross @ 2025-11-09 13:10 UTC (permalink / raw)
To: Vivian Wang; +Cc: musl, matthewcroughan
[-- Attachment #1: Type: text/plain, Size: 1484 bytes --]
Vivian Wang <wangruikang@iscas.ac.cn> writes:
> Grab the return address using an arch-specific wrapper dlopen calling a
> generic __dlopen (analogous to dlsym and __dlsym), and use it to find
> the dso to use as needed_by for load_library in __dlopen. This way, when
> a dso calls dlopen, the library is searched from *this* dso's rpath.
>
> This feature is used by shared libraries that dlopen on demand other
> shared libraries found in nonstandard paths.
>
> This makes the behavior of DT_RUNPATH match glibc better. Also, since we
> already use this behavior with libraries loaded with DT_NEEDED, adding
> support for dlopen makes it more consistent.
>
> By coincidence, both __dlsym and __dlopen take three arguments, the last
> of which is the return address. Therefore all of the arch-specific
> src/ldso/*/dlopen.s is just the corresponding dlsym.s with "dlsym"
> replaced by "dlopen".
>
> ---
> I am not subscribed to the mailing list, so please cc on reply. Thanks.
>
> I have primarily tested this on aarch64. I'm not sure whether this
> approach makes sense. Please advise.
>
> One observation is that musl falls back to using rpath entries from the
> entire needed_by chain, so this change in behavior shouldn't break
> existing working use cases.
Delightful to have just come across this issue myself and then discover
that a fix has already been proposed. :)
Fixes dlopen() from RUNPATH from a shared library for me on x86_64.
Tested-by: Alyssa Ross <hi@alyssa.is>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 227 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen
2025-10-17 10:50 [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen Vivian Wang
2025-11-09 13:10 ` Alyssa Ross
@ 2025-11-12 17:14 ` Rich Felker
2025-11-12 19:34 ` Demi Marie Obenour
2025-11-19 5:50 ` Vivian Wang
1 sibling, 2 replies; 7+ messages in thread
From: Rich Felker @ 2025-11-12 17:14 UTC (permalink / raw)
To: Vivian Wang; +Cc: musl, matthewcroughan
On Fri, Oct 17, 2025 at 06:50:52PM +0800, Vivian Wang wrote:
> Grab the return address using an arch-specific wrapper dlopen calling a
> generic __dlopen (analogous to dlsym and __dlsym), and use it to find
> the dso to use as needed_by for load_library in __dlopen. This way, when
> a dso calls dlopen, the library is searched from *this* dso's rpath.
>
> This feature is used by shared libraries that dlopen on demand other
> shared libraries found in nonstandard paths.
>
> This makes the behavior of DT_RUNPATH match glibc better. Also, since we
> already use this behavior with libraries loaded with DT_NEEDED, adding
> support for dlopen makes it more consistent.
>
> By coincidence, both __dlsym and __dlopen take three arguments, the last
> of which is the return address. Therefore all of the arch-specific
> src/ldso/*/dlopen.s is just the corresponding dlsym.s with "dlsym"
> replaced by "dlopen".
I'm not convinced that this is a good change. With dlsym, behaving
differently based on the call point is optional nonstandard
functionality triggered by passing RTLD_NEXT, and it already has
problems. In particular, the return address does not properly
determine who the caller is; it will be wrong if there's a tail call
to dlsym. We've considered in the past making a new definition for
RTLD_NEXT that uses the address of an object in the translation unit
that uses RTLD_NEXT, which would fix this but has other subtly
different behavior (like if RTLD_NEXT isn't passed directly to dlsym
but to a wrapper for it in a different library) so it's not clear if
it would be a worthwhile improvement.
In addition to violating least-surprise and having a nonstandard
behavior always active, changing dlopen as in this patch would have
the same tail-call issue, and would only give the behavior some
callers want if dlopen is directly called. For example if you had
loaded a library with its own rpath and instead of calling dlopen, it
called some other-library-provided abstraction for loading modules
that in turn indirectly called dlopen, its rpath would not get used.
This seems confusing and undesirable.
Rich
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen
2025-11-12 17:14 ` Rich Felker
@ 2025-11-12 19:34 ` Demi Marie Obenour
2025-11-13 0:49 ` Rich Felker
2025-11-13 22:51 ` Alyssa Ross
2025-11-19 5:50 ` Vivian Wang
1 sibling, 2 replies; 7+ messages in thread
From: Demi Marie Obenour @ 2025-11-12 19:34 UTC (permalink / raw)
To: musl, Rich Felker, Vivian Wang; +Cc: matthewcroughan
[-- Attachment #1.1.1: Type: text/plain, Size: 2472 bytes --]
On 11/12/25 12:14, Rich Felker wrote:
> On Fri, Oct 17, 2025 at 06:50:52PM +0800, Vivian Wang wrote:
>> Grab the return address using an arch-specific wrapper dlopen calling a
>> generic __dlopen (analogous to dlsym and __dlsym), and use it to find
>> the dso to use as needed_by for load_library in __dlopen. This way, when
>> a dso calls dlopen, the library is searched from *this* dso's rpath.
>>
>> This feature is used by shared libraries that dlopen on demand other
>> shared libraries found in nonstandard paths.
>>
>> This makes the behavior of DT_RUNPATH match glibc better. Also, since we
>> already use this behavior with libraries loaded with DT_NEEDED, adding
>> support for dlopen makes it more consistent.
>>
>> By coincidence, both __dlsym and __dlopen take three arguments, the last
>> of which is the return address. Therefore all of the arch-specific
>> src/ldso/*/dlopen.s is just the corresponding dlsym.s with "dlsym"
>> replaced by "dlopen".
>
> I'm not convinced that this is a good change. With dlsym, behaving
> differently based on the call point is optional nonstandard
> functionality triggered by passing RTLD_NEXT, and it already has
> problems. In particular, the return address does not properly
> determine who the caller is; it will be wrong if there's a tail call
> to dlsym. We've considered in the past making a new definition for
> RTLD_NEXT that uses the address of an object in the translation unit
> that uses RTLD_NEXT, which would fix this but has other subtly
> different behavior (like if RTLD_NEXT isn't passed directly to dlsym
> but to a wrapper for it in a different library) so it's not clear if
> it would be a worthwhile improvement.
>
> In addition to violating least-surprise and having a nonstandard
> behavior always active, changing dlopen as in this patch would have
> the same tail-call issue, and would only give the behavior some
> callers want if dlopen is directly called. For example if you had
> loaded a library with its own rpath and instead of calling dlopen, it
> called some other-library-provided abstraction for loading modules
> that in turn indirectly called dlopen, its rpath would not get used.
> This seems confusing and undesirable.
I don't think that this violates least-surprise. At least systemd
assumes glibc behavior, and I would not be surprised if other programs
and libraries do as well.
--
Sincerely,
Demi Marie Obenour (she/her/hers)
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 7253 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen
2025-11-12 19:34 ` Demi Marie Obenour
@ 2025-11-13 0:49 ` Rich Felker
2025-11-13 22:51 ` Alyssa Ross
1 sibling, 0 replies; 7+ messages in thread
From: Rich Felker @ 2025-11-13 0:49 UTC (permalink / raw)
To: Demi Marie Obenour; +Cc: musl, Vivian Wang, matthewcroughan
On Wed, Nov 12, 2025 at 02:34:25PM -0500, Demi Marie Obenour wrote:
> On 11/12/25 12:14, Rich Felker wrote:
> > On Fri, Oct 17, 2025 at 06:50:52PM +0800, Vivian Wang wrote:
> >> Grab the return address using an arch-specific wrapper dlopen calling a
> >> generic __dlopen (analogous to dlsym and __dlsym), and use it to find
> >> the dso to use as needed_by for load_library in __dlopen. This way, when
> >> a dso calls dlopen, the library is searched from *this* dso's rpath.
> >>
> >> This feature is used by shared libraries that dlopen on demand other
> >> shared libraries found in nonstandard paths.
> >>
> >> This makes the behavior of DT_RUNPATH match glibc better. Also, since we
> >> already use this behavior with libraries loaded with DT_NEEDED, adding
> >> support for dlopen makes it more consistent.
> >>
> >> By coincidence, both __dlsym and __dlopen take three arguments, the last
> >> of which is the return address. Therefore all of the arch-specific
> >> src/ldso/*/dlopen.s is just the corresponding dlsym.s with "dlsym"
> >> replaced by "dlopen".
> >
> > I'm not convinced that this is a good change. With dlsym, behaving
> > differently based on the call point is optional nonstandard
> > functionality triggered by passing RTLD_NEXT, and it already has
> > problems. In particular, the return address does not properly
> > determine who the caller is; it will be wrong if there's a tail call
> > to dlsym. We've considered in the past making a new definition for
> > RTLD_NEXT that uses the address of an object in the translation unit
> > that uses RTLD_NEXT, which would fix this but has other subtly
> > different behavior (like if RTLD_NEXT isn't passed directly to dlsym
> > but to a wrapper for it in a different library) so it's not clear if
> > it would be a worthwhile improvement.
> >
> > In addition to violating least-surprise and having a nonstandard
> > behavior always active, changing dlopen as in this patch would have
> > the same tail-call issue, and would only give the behavior some
> > callers want if dlopen is directly called. For example if you had
> > loaded a library with its own rpath and instead of calling dlopen, it
> > called some other-library-provided abstraction for loading modules
> > that in turn indirectly called dlopen, its rpath would not get used.
> > This seems confusing and undesirable.
>
> I don't think that this violates least-surprise. At least systemd
> assumes glibc behavior, and I would not be surprised if other programs
> and libraries do as well.
I think we're going by different definitions of least-surprise. What I
mean is that it is extremely abnormal, and impossible within what you
can implement in the standard language, for a function to behave
differently depending on where it's called from.
I would not use "systemd expects it" as evidence that a behavior is
"least surprise".
Rich
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen
2025-11-12 19:34 ` Demi Marie Obenour
2025-11-13 0:49 ` Rich Felker
@ 2025-11-13 22:51 ` Alyssa Ross
1 sibling, 0 replies; 7+ messages in thread
From: Alyssa Ross @ 2025-11-13 22:51 UTC (permalink / raw)
To: Demi Marie Obenour; +Cc: Rich Felker, Vivian Wang, matthewcroughan, musl
[-- Attachment #1: Type: text/plain, Size: 3332 bytes --]
[Resending as I somehow messed up the Cc line.]
On Wed, Nov 12, 2025 at 02:34:25PM -0500, Demi Marie Obenour wrote:
> On 11/12/25 12:14, Rich Felker wrote:
> > On Fri, Oct 17, 2025 at 06:50:52PM +0800, Vivian Wang wrote:
> >> Grab the return address using an arch-specific wrapper dlopen calling a
> >> generic __dlopen (analogous to dlsym and __dlsym), and use it to find
> >> the dso to use as needed_by for load_library in __dlopen. This way, when
> >> a dso calls dlopen, the library is searched from *this* dso's rpath.
> >>
> >> This feature is used by shared libraries that dlopen on demand other
> >> shared libraries found in nonstandard paths.
> >>
> >> This makes the behavior of DT_RUNPATH match glibc better. Also, since we
> >> already use this behavior with libraries loaded with DT_NEEDED, adding
> >> support for dlopen makes it more consistent.
> >>
> >> By coincidence, both __dlsym and __dlopen take three arguments, the last
> >> of which is the return address. Therefore all of the arch-specific
> >> src/ldso/*/dlopen.s is just the corresponding dlsym.s with "dlsym"
> >> replaced by "dlopen".
> >
> > I'm not convinced that this is a good change. With dlsym, behaving
> > differently based on the call point is optional nonstandard
> > functionality triggered by passing RTLD_NEXT, and it already has
> > problems. In particular, the return address does not properly
> > determine who the caller is; it will be wrong if there's a tail call
> > to dlsym. We've considered in the past making a new definition for
> > RTLD_NEXT that uses the address of an object in the translation unit
> > that uses RTLD_NEXT, which would fix this but has other subtly
> > different behavior (like if RTLD_NEXT isn't passed directly to dlsym
> > but to a wrapper for it in a different library) so it's not clear if
> > it would be a worthwhile improvement.
> >
> > In addition to violating least-surprise and having a nonstandard
> > behavior always active, changing dlopen as in this patch would have
> > the same tail-call issue, and would only give the behavior some
> > callers want if dlopen is directly called. For example if you had
> > loaded a library with its own rpath and instead of calling dlopen, it
> > called some other-library-provided abstraction for loading modules
> > that in turn indirectly called dlopen, its rpath would not get used.
> > This seems confusing and undesirable.
>
> I don't think that this violates least-surprise. At least systemd
> assumes glibc behavior, and I would not be surprised if other programs
> and libraries do as well.
Technically speaking I don't think it's systemd that assumes Glibc
behaviour. systemd just puts .note.dlopen sections in its executables
and libraries, and it's up to the packaging system to use that metadata
to ensure the mentioned shared libraries are available if desired.
In the scenario I think we're all coming from, it's Nixpkgs'
autoPatchelfHook that has turned those into DT_RUNPATH entries.
Presumably this was only tested with Glibc, and we're only discovering
it now because people are getting more adventurous with the combination
of Nixpkgs, musl, and systemd.
Given what I've read here, perhaps the easiest way forward would be to
get systemd to (perhaps optionally) use absolute paths for dlopen() of
optional dependencies like this.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen
2025-11-12 17:14 ` Rich Felker
2025-11-12 19:34 ` Demi Marie Obenour
@ 2025-11-19 5:50 ` Vivian Wang
1 sibling, 0 replies; 7+ messages in thread
From: Vivian Wang @ 2025-11-19 5:50 UTC (permalink / raw)
To: Rich Felker, Vivian Wang; +Cc: musl, matthewcroughan
On 11/13/25 01:14, Rich Felker wrote:
> On Fri, Oct 17, 2025 at 06:50:52PM +0800, Vivian Wang wrote:
>> Grab the return address using an arch-specific wrapper dlopen calling a
>> generic __dlopen (analogous to dlsym and __dlsym), and use it to find
>> the dso to use as needed_by for load_library in __dlopen. This way, when
>> a dso calls dlopen, the library is searched from *this* dso's rpath.
>>
>> This feature is used by shared libraries that dlopen on demand other
>> shared libraries found in nonstandard paths.
>>
>> This makes the behavior of DT_RUNPATH match glibc better. Also, since we
>> already use this behavior with libraries loaded with DT_NEEDED, adding
>> support for dlopen makes it more consistent.
>>
>> By coincidence, both __dlsym and __dlopen take three arguments, the last
>> of which is the return address. Therefore all of the arch-specific
>> src/ldso/*/dlopen.s is just the corresponding dlsym.s with "dlsym"
>> replaced by "dlopen".
> I'm not convinced that this is a good change. With dlsym, behaving
> differently based on the call point is optional nonstandard
> functionality triggered by passing RTLD_NEXT, and it already has
> problems. In particular, the return address does not properly
> determine who the caller is; it will be wrong if there's a tail call
> to dlsym. We've considered in the past making a new definition for
> RTLD_NEXT that uses the address of an object in the translation unit
> that uses RTLD_NEXT, which would fix this but has other subtly
> different behavior (like if RTLD_NEXT isn't passed directly to dlsym
> but to a wrapper for it in a different library) so it's not clear if
> it would be a worthwhile improvement.
>
> In addition to violating least-surprise and having a nonstandard
> behavior always active, changing dlopen as in this patch would have
> the same tail-call issue, and would only give the behavior some
> callers want if dlopen is directly called. For example if you had
> loaded a library with its own rpath and instead of calling dlopen, it
> called some other-library-provided abstraction for loading modules
> that in turn indirectly called dlopen, its rpath would not get used.
> This seems confusing and undesirable.
Given the mentioned considerations, I accept that this will never be
implemented in Musl, and that programs wishing to dlopen from some
explicit path, including, as mentioned, those packaged in Nixpkgs,
should do so by passing the full pass to dlopen().
Thanks,
Vivian "dramforever" Wang
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-11-19 8:52 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-10-17 10:50 [musl] [PATCH] ldso: Use rpath of dso of caller in dlopen Vivian Wang
2025-11-09 13:10 ` Alyssa Ross
2025-11-12 17:14 ` Rich Felker
2025-11-12 19:34 ` Demi Marie Obenour
2025-11-13 0:49 ` Rich Felker
2025-11-13 22:51 ` Alyssa Ross
2025-11-19 5:50 ` Vivian Wang
Code repositories for project(s) associated with this public inbox
https://git.vuxu.org/mirror/musl/
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).