path: root/src/ldso
diff options
authorRich Felker <>2015-11-11 17:34:17 -0500
committerRich Felker <>2015-11-11 17:40:27 -0500
commit9439ebd7668182a1c6213ad1a5011bdc71585b5f (patch)
tree5166f5c1b09e0c9115b582897633cc80331f4c81 /src/ldso
parenta946e8117ed51dd771bd8cac3575fc28a0399a32 (diff)
fix dynamic loader library mapping for nommu systems
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.
Diffstat (limited to 'src/ldso')
1 files changed, 15 insertions, 4 deletions
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index a6484dd5..5fbe2bb5 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -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);
+ ? mmap((void *)addr_min, map_len, PROT_READ|PROT_WRITE|PROT_EXEC,
+ : 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;