don't fail to map library/executable with zero-length segment maps
[musl] / ldso / dynlink.c
index 6468f20..aaadcce 100644 (file)
@@ -1,6 +1,5 @@
 #define _GNU_SOURCE
 #define SYSCALL_NO_TLS 1
-#include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <semaphore.h>
 #include <sys/membarrier.h>
 #include "pthread_impl.h"
+#include "fork_impl.h"
 #include "libc.h"
 #include "dynlink.h"
-#include "malloc_impl.h"
+
+#define malloc __libc_malloc
+#define calloc __libc_calloc
+#define realloc __libc_realloc
+#define free __libc_free
 
 static void error(const char *, ...);
 
@@ -79,7 +83,7 @@ struct dso {
        struct dso **deps, *needed_by;
        size_t ndeps_direct;
        size_t next_dep;
-       int ctor_visitor;
+       pthread_t ctor_visitor;
        char *rpath_orig, *rpath;
        struct tls_module tls;
        size_t tls_id;
@@ -557,10 +561,25 @@ static void reclaim_gaps(struct dso *dso)
        }
 }
 
+static ssize_t read_loop(int fd, void *p, size_t n)
+{
+       for (size_t i=0; i<n; ) {
+               ssize_t l = read(fd, (char *)p+i, n-i);
+               if (l<0) {
+                       if (errno==EINTR) continue;
+                       else return -1;
+               }
+               if (l==0) return i;
+               i += l;
+       }
+       return n;
+}
+
 static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t off)
 {
        static int no_map_fixed;
        char *q;
+       if (!n) return p;
        if (!no_map_fixed) {
                q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);
                if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL)
@@ -1061,13 +1080,17 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
                                snprintf(etc_ldso_path, sizeof etc_ldso_path,
                                        "%.*s/etc/ld-musl-" LDSO_ARCH ".path",
                                        (int)prefix_len, prefix);
-                               FILE *f = fopen(etc_ldso_path, "rbe");
-                               if (f) {
-                                       if (getdelim(&sys_path, (size_t[1]){0}, 0, f) <= 0) {
+                               fd = open(etc_ldso_path, O_RDONLY|O_CLOEXEC);
+                               if (fd>=0) {
+                                       size_t n = 0;
+                                       if (!fstat(fd, &st)) n = st.st_size;
+                                       if ((sys_path = malloc(n+1)))
+                                               sys_path[n] = 0;
+                                       if (!sys_path || read_loop(fd, sys_path, n)<0) {
                                                free(sys_path);
                                                sys_path = "";
                                        }
-                                       fclose(f);
+                                       close(fd);
                                } else if (errno != ENOENT) {
                                        sys_path = "";
                                }
@@ -1379,7 +1402,7 @@ void __libc_exit_fini()
 {
        struct dso *p;
        size_t dyn[DYN_CNT];
-       int self = __pthread_self()->tid;
+       pthread_t self = __pthread_self();
 
        /* Take both locks before setting shutting_down, so that
         * either lock is sufficient to read its value. The lock
@@ -1405,6 +1428,17 @@ void __libc_exit_fini()
        }
 }
 
+void __ldso_atfork(int who)
+{
+       if (who<0) {
+               pthread_rwlock_wrlock(&lock);
+               pthread_mutex_lock(&init_fini_lock);
+       } else {
+               pthread_mutex_unlock(&init_fini_lock);
+               pthread_rwlock_unlock(&lock);
+       }
+}
+
 static struct dso **queue_ctors(struct dso *dso)
 {
        size_t cnt, qpos, spos, i;
@@ -1463,6 +1497,13 @@ static struct dso **queue_ctors(struct dso *dso)
        }
        queue[qpos] = 0;
        for (i=0; i<qpos; i++) queue[i]->mark = 0;
+       for (i=0; i<qpos; i++)
+               if (queue[i]->ctor_visitor && queue[i]->ctor_visitor->tid < 0) {
+                       error("State of %s is inconsistent due to multithreaded fork\n",
+                               queue[i]->name);
+                       free(queue);
+                       if (runtime) longjmp(*rtld_fail, 1);
+               }
 
        return queue;
 }
@@ -1471,7 +1512,7 @@ static void do_init_fini(struct dso **queue)
 {
        struct dso *p;
        size_t dyn[DYN_CNT], i;
-       int self = __pthread_self()->tid;
+       pthread_t self = __pthread_self();
 
        pthread_mutex_lock(&init_fini_lock);
        for (i=0; (p=queue[i]); i++) {
@@ -1580,7 +1621,7 @@ static void install_new_tls(void)
 
        /* Install new dtv for each thread. */
        for (j=0, td=self; !j || td!=self; j++, td=td->next) {
-               td->dtv = td->dtv_copy = newdtv[j];
+               td->dtv = newdtv[j];
        }
 
        __tl_unlock();
@@ -1936,6 +1977,8 @@ void __dls3(size_t *sp, size_t *auxv)
         * possibility of incomplete replacement. */
        if (find_sym(head, "malloc", 1).dso != &ldso)
                __malloc_replaced = 1;
+       if (find_sym(head, "aligned_alloc", 1).dso != &ldso)
+               __aligned_alloc_replaced = 1;
 
        /* Switch to runtime mode: any further failures in the dynamic
         * linker are a reportable failure rather than a fatal startup
@@ -1946,7 +1989,7 @@ void __dls3(size_t *sp, size_t *auxv)
        debug.bp = dl_debug_state;
        debug.head = head;
        debug.base = ldso.base;
-       debug.state = 0;
+       debug.state = RT_CONSISTENT;
        _dl_debug_state();
 
        if (replace_argv0) argv[0] = replace_argv0;
@@ -1995,6 +2038,9 @@ void *dlopen(const char *file, int mode)
        pthread_rwlock_wrlock(&lock);
        __inhibit_ptc();
 
+       debug.state = RT_ADD;
+       _dl_debug_state();
+
        p = 0;
        if (shutting_down) {
                error("Cannot dlopen while program is exiting.");
@@ -2054,8 +2100,9 @@ void *dlopen(const char *file, int mode)
        load_deps(p);
        extend_bfs_deps(p);
        pthread_mutex_lock(&init_fini_lock);
-       if (!p->constructed) ctor_queue = queue_ctors(p);
+       int constructed = p->constructed;
        pthread_mutex_unlock(&init_fini_lock);
+       if (!constructed) ctor_queue = queue_ctors(p);
        if (!p->relocated && (mode & RTLD_LAZY)) {
                prepare_lazy(p);
                for (i=0; p->deps[i]; i++)
@@ -2087,9 +2134,10 @@ void *dlopen(const char *file, int mode)
        update_tls_size();
        if (tls_cnt != orig_tls_cnt)
                install_new_tls();
-       _dl_debug_state();
        orig_tail = tail;
 end:
+       debug.state = RT_CONSISTENT;
+       _dl_debug_state();
        __release_ptc();
        if (p) gencnt++;
        pthread_rwlock_unlock(&lock);