fix dynamic loader library mapping for nommu systems
authorRich Felker <dalias@aerifal.cx>
Wed, 11 Nov 2015 22:34:17 +0000 (17:34 -0500)
committerRich Felker <dalias@aerifal.cx>
Wed, 11 Nov 2015 22:40:27 +0000 (17:40 -0500)
on linux/nommu, non-writable private mappings of files may actually
use memory shared with other processes or the fs cache. the old nommu
loader code (used when mmap with MAP_FIXED fails) simply wrote over
top of the original file mapping, possibly clobbering this shared
memory. no such breakage was observed in practice, but it should have
been possible.

the new code starts by mapping anonymous writable memory on archs that
might support nommu, then maps load segments over top of it, falling
back to read if MAP_FIXED fails. we use an anonymous map rather than a
writable file map to avoid reading more data from disk than needed.
since pages cannot be loaded lazily on fault, in case of large
data/bss, mapping the full file may read a lot of data that will
subsequently be thrown away when processing additional LOAD segments.
as a result, we cannot skip the first LOAD segment when operating in
this mode.

these changes affect only non-FDPIC nommu support.

arch/sh/reloc.h
src/internal/dynlink.h
src/ldso/dynlink.c

index d4fe348cd4d80ff829e1b7a78dae1f43697bafc0..0238ce075ba4d98718847fa38229259869dfeff6 100644 (file)
@@ -32,6 +32,8 @@
 #define REL_DTPOFF      R_SH_TLS_DTPOFF32
 #define REL_TPOFF       R_SH_TLS_TPOFF32
 
+#define DL_NOMMU_SUPPORT 1
+
 #if __SH_FDPIC__
 #define REL_FUNCDESC    R_SH_FUNCDESC
 #define REL_FUNCDESC_VAL R_SH_FUNCDESC_VALUE
index 86f379e66813b13d3fe6eefe62f7af6e82805c35..9c494e43966e0d7f0c80b621c6178f5cc4871a6d 100644 (file)
@@ -64,6 +64,10 @@ struct fdpic_dummy_loadmap {
 #define DL_FDPIC 0
 #endif
 
+#ifndef DL_NOMMU_SUPPORT
+#define DL_NOMMU_SUPPORT 0
+#endif
+
 #if !DL_FDPIC
 #define IS_RELATIVE(x,s) ( \
        (R_TYPE(x) == REL_RELATIVE) || \
index a6484dd5a9b7b1347fefb53356eab701ca759b6a..5fbe2bb5471ffe8824778bc5f2a8adc713552ef5 100644 (file)
@@ -482,8 +482,14 @@ static void reclaim_gaps(struct dso *dso)
 
 static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t off)
 {
-       char *q = mmap(p, n, prot, flags, fd, off);
-       if (q != MAP_FAILED || errno != EINVAL) return q;
+       static int no_map_fixed;
+       char *q;
+       if (!no_map_fixed) {
+               q = mmap(p, n, prot, flags|MAP_FIXED, fd, off);
+               if (!DL_NOMMU_SUPPORT || q != MAP_FAILED || errno != EINVAL)
+                       return q;
+               no_map_fixed = 1;
+       }
        /* Fallbacks for MAP_FIXED failure on NOMMU kernels. */
        if (flags & MAP_ANONYMOUS) {
                memset(p, 0, n);
@@ -631,7 +637,11 @@ static void *map_library(int fd, struct dso *dso)
         * the length of the file. This is okay because we will not
         * use the invalid part; we just need to reserve the right
         * amount of virtual address space to map over later. */
-       map = mmap((void *)addr_min, map_len, prot, MAP_PRIVATE, fd, off_start);
+       map = DL_NOMMU_SUPPORT
+               ? mmap((void *)addr_min, map_len, PROT_READ|PROT_WRITE|PROT_EXEC,
+                       MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
+               : mmap((void *)addr_min, map_len, prot,
+                       MAP_PRIVATE, fd, off_start);
        if (map==MAP_FAILED) goto error;
        dso->map = map;
        dso->map_len = map_len;
@@ -656,7 +666,8 @@ static void *map_library(int fd, struct dso *dso)
                        dso->phentsize = eh->e_phentsize;
                }
                /* Reuse the existing mapping for the lowest-address LOAD */
-               if ((ph->p_vaddr & -PAGE_SIZE) == addr_min) continue;
+               if ((ph->p_vaddr & -PAGE_SIZE) == addr_min && !DL_NOMMU_SUPPORT)
+                       continue;
                this_min = ph->p_vaddr & -PAGE_SIZE;
                this_max = ph->p_vaddr+ph->p_memsz+PAGE_SIZE-1 & -PAGE_SIZE;
                off_start = ph->p_offset & -PAGE_SIZE;