mailing list of musl libc
 help / color / mirror / code / Atom feed
* [musl] [RFC PATCH 0/2] ldso: Add fdlopen
@ 2023-05-08 18:14 Pedro Falcato
  2023-05-08 18:14 ` [musl] [RFC PATCH 1/2] dynlink: Reorganize library loading Pedro Falcato
  2023-05-08 18:14 ` [musl] [RFC PATCH 2/2] ldso: Add fdlopen Pedro Falcato
  0 siblings, 2 replies; 3+ messages in thread
From: Pedro Falcato @ 2023-05-08 18:14 UTC (permalink / raw)
  To: musl; +Cc: Pedro Falcato

Add an implementation of fdlopen as vaguely discussed on IRC.
This implementation Should(tm) be entirely compatible with FreeBSD fdlopen
(as documented in fdlopen(3)).

This is merely an RFC. Feedback is entirely welcome.

Pedro Falcato (2):
  dynlink: Reorganize library loading
  ldso: Add fdlopen

 include/dlfcn.h    |   1 +
 ldso/dynlink.c     | 245 +++++++++++++++++++++++++++------------------
 src/ldso/fdlopen.c |  10 ++
 3 files changed, 157 insertions(+), 99 deletions(-)
 create mode 100644 src/ldso/fdlopen.c

-- 
2.40.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [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

end of thread, other threads:[~2023-05-08 18:14 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [musl] [RFC PATCH 2/2] ldso: Add fdlopen Pedro Falcato

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).