* [musl] [RFC PATCH 1/2] dynlink: Reorganize library loading
2023-05-08 18:14 [musl] [RFC PATCH 0/2] ldso: Add fdlopen Pedro Falcato
@ 2023-05-08 18:14 ` Pedro Falcato
2023-05-08 18:14 ` [musl] [RFC PATCH 2/2] ldso: Add fdlopen Pedro Falcato
1 sibling, 0 replies; 3+ messages in thread
From: Pedro Falcato @ 2023-05-08 18:14 UTC (permalink / raw)
To: musl; +Cc: Pedro Falcato
Separate the core of library loading from load_library, in a way that
can be reused by codepaths that do not need path -> library resolution.
Signed-off-by: Pedro Falcato <pedro.falcato@gmail.com>
---
ldso/dynlink.c | 202 ++++++++++++++++++++++++++-----------------------
1 file changed, 107 insertions(+), 95 deletions(-)
diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 2b8c382b..2272b768 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -988,16 +988,118 @@ static void makefuncdescs(struct dso *p)
}
}
+static struct dso *load_library(const char *name, struct dso *needed_by);
+
+static struct dso *load_library_core(int fd, struct dso *needed_by, const char *pathname, const char *name)
+{
+ struct dso *p;
+ struct stat st;
+ size_t alloc_size;
+ int n_th = 0;
+ unsigned char *map;
+ struct dso temp_dso = {0};
+
+ if (fd < 0) return 0;
+ if (fstat(fd, &st) < 0) {
+ close(fd);
+ return 0;
+ }
+ for (p=head->next; p; p=p->next) {
+ if (p->dev == st.st_dev && p->ino == st.st_ino) {
+ /* If this library was previously loaded with a
+ * pathname but a search found the same inode,
+ * setup its shortname so it can be found by name. */
+ if (!p->shortname && pathname != name)
+ p->shortname = strrchr(p->name, '/')+1;
+ close(fd);
+ return p;
+ }
+ }
+ map = noload ? 0 : map_library(fd, &temp_dso);
+ close(fd);
+ if (!map) return 0;
+
+ /* Avoid the danger of getting two versions of libc mapped into the
+ * same process when an absolute pathname was used. The symbols
+ * checked are chosen to catch both musl and glibc, and to avoid
+ * false positives from interposition-hack libraries. */
+ decode_dyn(&temp_dso);
+ if (find_sym(&temp_dso, "__libc_start_main", 1).sym &&
+ find_sym(&temp_dso, "stdin", 1).sym) {
+ unmap_library(&temp_dso);
+ return load_library("libc.so", needed_by);
+ }
+ /* Past this point, if we haven't reached runtime yet, ldso has
+ * committed either to use the mapped library or to abort execution.
+ * Unmapping is not possible, so we can safely reclaim gaps. */
+ if (!runtime) reclaim_gaps(&temp_dso);
+
+ /* Allocate storage for the new DSO. When there is TLS, this
+ * storage must include a reservation for all pre-existing
+ * threads to obtain copies of both the new TLS, and an
+ * extended DTV capable of storing an additional slot for
+ * the newly-loaded DSO. */
+ alloc_size = sizeof *p + strlen(pathname) + 1;
+ if (runtime && temp_dso.tls.image) {
+ size_t per_th = temp_dso.tls.size + temp_dso.tls.align
+ + sizeof(void *) * (tls_cnt+3);
+ n_th = libc.threads_minus_1 + 1;
+ if (n_th > SSIZE_MAX / per_th) alloc_size = SIZE_MAX;
+ else alloc_size += n_th * per_th;
+ }
+ p = calloc(1, alloc_size);
+ if (!p) {
+ unmap_library(&temp_dso);
+ return 0;
+ }
+ memcpy(p, &temp_dso, sizeof temp_dso);
+ p->dev = st.st_dev;
+ p->ino = st.st_ino;
+ p->needed_by = needed_by;
+ p->name = p->buf;
+ p->runtime_loaded = runtime;
+ strcpy(p->name, pathname);
+ /* Add a shortname only if name arg was not an explicit pathname. */
+ if (pathname != name) p->shortname = strrchr(p->name, '/')+1;
+ if (p->tls.image) {
+ p->tls_id = ++tls_cnt;
+ tls_align = MAXP2(tls_align, p->tls.align);
+#ifdef TLS_ABOVE_TP
+ p->tls.offset = tls_offset + ( (p->tls.align-1) &
+ (-tls_offset + (uintptr_t)p->tls.image) );
+ tls_offset = p->tls.offset + p->tls.size;
+#else
+ tls_offset += p->tls.size + p->tls.align - 1;
+ tls_offset -= (tls_offset + (uintptr_t)p->tls.image)
+ & (p->tls.align-1);
+ p->tls.offset = tls_offset;
+#endif
+ p->new_dtv = (void *)(-sizeof(size_t) &
+ (uintptr_t)(p->name+strlen(p->name)+sizeof(size_t)));
+ p->new_tls = (void *)(p->new_dtv + n_th*(tls_cnt+1));
+ if (tls_tail) tls_tail->next = &p->tls;
+ else libc.tls_head = &p->tls;
+ tls_tail = &p->tls;
+ }
+
+ tail->next = p;
+ p->prev = tail;
+ tail = p;
+
+ if (DL_FDPIC) makefuncdescs(p);
+
+ if (ldd_mode) dprintf(1, "\t%s => %s (%p)\n", name, pathname, p->base);
+
+ return p;
+}
+
static struct dso *load_library(const char *name, struct dso *needed_by)
{
char buf[2*NAME_MAX+2];
const char *pathname;
- unsigned char *map;
- struct dso *p, temp_dso = {0};
+ struct dso *p;
int fd;
struct stat st;
- size_t alloc_size;
- int n_th = 0;
int is_self = 0;
if (!*name) {
@@ -1100,98 +1202,8 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
}
pathname = buf;
}
- if (fd < 0) return 0;
- if (fstat(fd, &st) < 0) {
- close(fd);
- return 0;
- }
- for (p=head->next; p; p=p->next) {
- if (p->dev == st.st_dev && p->ino == st.st_ino) {
- /* If this library was previously loaded with a
- * pathname but a search found the same inode,
- * setup its shortname so it can be found by name. */
- if (!p->shortname && pathname != name)
- p->shortname = strrchr(p->name, '/')+1;
- close(fd);
- return p;
- }
- }
- map = noload ? 0 : map_library(fd, &temp_dso);
- close(fd);
- if (!map) return 0;
-
- /* Avoid the danger of getting two versions of libc mapped into the
- * same process when an absolute pathname was used. The symbols
- * checked are chosen to catch both musl and glibc, and to avoid
- * false positives from interposition-hack libraries. */
- decode_dyn(&temp_dso);
- if (find_sym(&temp_dso, "__libc_start_main", 1).sym &&
- find_sym(&temp_dso, "stdin", 1).sym) {
- unmap_library(&temp_dso);
- return load_library("libc.so", needed_by);
- }
- /* Past this point, if we haven't reached runtime yet, ldso has
- * committed either to use the mapped library or to abort execution.
- * Unmapping is not possible, so we can safely reclaim gaps. */
- if (!runtime) reclaim_gaps(&temp_dso);
-
- /* Allocate storage for the new DSO. When there is TLS, this
- * storage must include a reservation for all pre-existing
- * threads to obtain copies of both the new TLS, and an
- * extended DTV capable of storing an additional slot for
- * the newly-loaded DSO. */
- alloc_size = sizeof *p + strlen(pathname) + 1;
- if (runtime && temp_dso.tls.image) {
- size_t per_th = temp_dso.tls.size + temp_dso.tls.align
- + sizeof(void *) * (tls_cnt+3);
- n_th = libc.threads_minus_1 + 1;
- if (n_th > SSIZE_MAX / per_th) alloc_size = SIZE_MAX;
- else alloc_size += n_th * per_th;
- }
- p = calloc(1, alloc_size);
- if (!p) {
- unmap_library(&temp_dso);
- return 0;
- }
- memcpy(p, &temp_dso, sizeof temp_dso);
- p->dev = st.st_dev;
- p->ino = st.st_ino;
- p->needed_by = needed_by;
- p->name = p->buf;
- p->runtime_loaded = runtime;
- strcpy(p->name, pathname);
- /* Add a shortname only if name arg was not an explicit pathname. */
- if (pathname != name) p->shortname = strrchr(p->name, '/')+1;
- if (p->tls.image) {
- p->tls_id = ++tls_cnt;
- tls_align = MAXP2(tls_align, p->tls.align);
-#ifdef TLS_ABOVE_TP
- p->tls.offset = tls_offset + ( (p->tls.align-1) &
- (-tls_offset + (uintptr_t)p->tls.image) );
- tls_offset = p->tls.offset + p->tls.size;
-#else
- tls_offset += p->tls.size + p->tls.align - 1;
- tls_offset -= (tls_offset + (uintptr_t)p->tls.image)
- & (p->tls.align-1);
- p->tls.offset = tls_offset;
-#endif
- p->new_dtv = (void *)(-sizeof(size_t) &
- (uintptr_t)(p->name+strlen(p->name)+sizeof(size_t)));
- p->new_tls = (void *)(p->new_dtv + n_th*(tls_cnt+1));
- if (tls_tail) tls_tail->next = &p->tls;
- else libc.tls_head = &p->tls;
- tls_tail = &p->tls;
- }
-
- tail->next = p;
- p->prev = tail;
- tail = p;
-
- if (DL_FDPIC) makefuncdescs(p);
- if (ldd_mode) dprintf(1, "\t%s => %s (%p)\n", name, pathname, p->base);
-
- return p;
+ return load_library_core(fd, needed_by, pathname, name);
}
static void load_direct_deps(struct dso *p)
--
2.40.1
^ permalink raw reply [flat|nested] 3+ messages in thread
* [musl] [RFC PATCH 2/2] ldso: Add fdlopen
2023-05-08 18:14 [musl] [RFC PATCH 0/2] ldso: Add fdlopen Pedro Falcato
2023-05-08 18:14 ` [musl] [RFC PATCH 1/2] dynlink: Reorganize library loading Pedro Falcato
@ 2023-05-08 18:14 ` Pedro Falcato
1 sibling, 0 replies; 3+ messages in thread
From: Pedro Falcato @ 2023-05-08 18:14 UTC (permalink / raw)
To: musl; +Cc: Pedro Falcato
Add fdlopen as specified and implemented in FreeBSD (see fdlopen(3) in
the FreeBSD manpages).
Signed-off-by: Pedro Falcato <pedro.falcato@gmail.com>
---
include/dlfcn.h | 1 +
ldso/dynlink.c | 43 +++++++++++++++++++++++++++++++++++++++----
src/ldso/fdlopen.c | 10 ++++++++++
3 files changed, 50 insertions(+), 4 deletions(-)
create mode 100644 src/ldso/fdlopen.c
diff --git a/include/dlfcn.h b/include/dlfcn.h
index 13ab71dd..6a01106d 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -33,6 +33,7 @@ typedef struct {
} Dl_info;
int dladdr(const void *, Dl_info *);
int dlinfo(void *, int, void *);
+void *fdlopen(int, int);
#endif
#if _REDIR_TIME64
diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 2272b768..161dde52 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -2040,7 +2040,7 @@ static void prepare_lazy(struct dso *p)
lazy_head = p;
}
-void *dlopen(const char *file, int mode)
+static void *dlopen_internal(int fd, const char *file, int mode)
{
struct dso *volatile p, *orig_tail, *orig_syms_tail, *orig_lazy_head, *next;
struct tls_module *orig_tls_tail;
@@ -2050,8 +2050,6 @@ void *dlopen(const char *file, int mode)
jmp_buf jb;
struct dso **volatile ctor_queue = 0;
- if (!file) return head;
-
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
pthread_rwlock_wrlock(&lock);
__inhibit_ptc();
@@ -2104,7 +2102,32 @@ void *dlopen(const char *file, int mode)
tail->next = 0;
p = 0;
goto end;
- } else p = load_library(file, head);
+ } else {
+ if (fd != -1) {
+ /* We were called from fdlopen. First, we must dup the fd (as FreeBSD does). */
+ fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+ if (fd < 0) {
+ error("Failed to duplicate file descriptor: %m");
+ goto end;
+ }
+
+ /* Now try to figure out the path using procfs. If we fail, use a fallback. */
+ char path[PATH_MAX + 1];
+ const char *pathname = path;
+
+ __procfdname(path, fd);
+ ssize_t st = readlink(path, path, PATH_MAX);
+ if (st < 0) {
+ pathname = "<anon_fdlopen>";
+ } else {
+ path[st] = '\0';
+ }
+
+ p = load_library_core(fd, head, pathname, pathname);
+ } else {
+ p = load_library(file, head);
+ }
+ }
if (!p) {
error(noload ?
@@ -2167,6 +2190,18 @@ end:
return p;
}
+void *fdlopen(int fd, int mode)
+{
+ if (fd == -1) return head;
+ return dlopen_internal(fd, 0, mode);
+}
+
+void *dlopen(const char *file, int mode)
+{
+ if (!file) return head;
+ return dlopen_internal(-1, file, mode);
+}
+
hidden int __dl_invalid_handle(void *h)
{
struct dso *p;
diff --git a/src/ldso/fdlopen.c b/src/ldso/fdlopen.c
new file mode 100644
index 00000000..a9380b57
--- /dev/null
+++ b/src/ldso/fdlopen.c
@@ -0,0 +1,10 @@
+#include <dlfcn.h>
+#include "dynlink.h"
+
+static void *stub_fdlopen(int fd, int mode)
+{
+ __dl_seterr("Dynamic loading not supported");
+ return 0;
+}
+
+weak_alias(stub_fdlopen, fdlopen);
--
2.40.1
^ permalink raw reply [flat|nested] 3+ messages in thread