diff options
Diffstat (limited to 'src/ldso')
-rw-r--r-- | src/ldso/dynlink.c | 63 |
1 files changed, 54 insertions, 9 deletions
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index 4903dbd2..2c822b64 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -502,6 +502,22 @@ static void *mmap_fixed(void *p, size_t n, int prot, int flags, int fd, off_t of return p; } +static void unmap_library(struct dso *dso) +{ + if (dso->loadmap) { + size_t i; + for (i=0; i<dso->loadmap->nsegs; i++) { + if (!dso->loadmap->segs[i].p_memsz) + continue; + munmap((void *)dso->loadmap->segs[i].addr, + dso->loadmap->segs[i].p_memsz); + } + free(dso->loadmap); + } else if (dso->map && dso->map_len) { + munmap(dso->map, dso->map_len); + } +} + static void *map_library(int fd, struct dso *dso) { Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)]; @@ -509,6 +525,7 @@ static void *map_library(int fd, struct dso *dso) size_t phsize; size_t addr_min=SIZE_MAX, addr_max=0, map_len; size_t this_min, this_max; + size_t nsegs = 0; off_t off_start; Ehdr *eh; Phdr *ph, *ph0; @@ -552,6 +569,7 @@ static void *map_library(int fd, struct dso *dso) dso->relro_end = (ph->p_vaddr + ph->p_memsz) & -PAGE_SIZE; } if (ph->p_type != PT_LOAD) continue; + nsegs++; if (ph->p_vaddr < addr_min) { addr_min = ph->p_vaddr; off_start = ph->p_offset; @@ -564,6 +582,33 @@ static void *map_library(int fd, struct dso *dso) } } if (!dyn) goto noexec; + if (DL_FDPIC && !(eh->e_flags & FDPIC_CONSTDISP_FLAG)) { + dso->loadmap = calloc(1, sizeof *dso->loadmap + + nsegs * sizeof *dso->loadmap->segs); + if (!dso->loadmap) goto error; + dso->loadmap->nsegs = nsegs; + for (ph=ph0, i=0; i<nsegs; ph=(void *)((char *)ph+eh->e_phentsize)) { + if (ph->p_type != PT_LOAD) continue; + prot = (((ph->p_flags&PF_R) ? PROT_READ : 0) | + ((ph->p_flags&PF_W) ? PROT_WRITE: 0) | + ((ph->p_flags&PF_X) ? PROT_EXEC : 0)); + map = mmap(0, ph->p_memsz + (ph->p_vaddr & PAGE_SIZE-1), + prot, (prot&PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED, + fd, ph->p_offset & -PAGE_SIZE); + if (map == MAP_FAILED) { + unmap_library(dso); + goto error; + } + dso->loadmap->segs[i].addr = (size_t)map + + (ph->p_vaddr & PAGE_SIZE-1); + dso->loadmap->segs[i].p_vaddr = ph->p_vaddr; + dso->loadmap->segs[i].p_memsz = ph->p_memsz; + i++; + } + map = (void *)dso->loadmap->segs[0].addr; + map_len = 0; + goto done_mapping; + } addr_max += PAGE_SIZE-1; addr_max &= -PAGE_SIZE; addr_min &= -PAGE_SIZE; @@ -575,6 +620,8 @@ static void *map_library(int fd, struct dso *dso) * amount of virtual address space to map over later. */ map = 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; /* If the loaded file is not relocatable and the requested address is * not available, then the load operation must fail. */ if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) { @@ -620,18 +667,17 @@ static void *map_library(int fd, struct dso *dso) goto error; break; } - dso->map = map; - dso->map_len = map_len; +done_mapping: dso->base = base; - dso->dynv = (void *)(base+dyn); - if (dso->tls_size) dso->tls_image = (void *)(base+tls_image); + dso->dynv = laddr(dso, dyn); + if (dso->tls_size) dso->tls_image = laddr(dso, tls_image); if (!runtime) reclaim_gaps(dso); free(allocated_buf); return map; noexec: errno = ENOEXEC; error: - if (map!=MAP_FAILED) munmap(map, map_len); + if (map!=MAP_FAILED) unmap_library(dso); free(allocated_buf); return 0; } @@ -950,7 +996,7 @@ static struct dso *load_library(const char *name, struct dso *needed_by) } p = calloc(1, alloc_size); if (!p) { - munmap(map, temp_dso.map_len); + unmap_library(&temp_dso); return 0; } memcpy(p, &temp_dso, sizeof temp_dso); @@ -1620,17 +1666,16 @@ void *dlopen(const char *file, int mode) p->deps[i]->global = 0; for (p=orig_tail->next; p; p=next) { next = p->next; - munmap(p->map, p->map_len); while (p->td_index) { void *tmp = p->td_index->next; free(p->td_index); p->td_index = tmp; } - if (p->funcdescs) - free(p->funcdescs); + free(p->funcdescs); if (p->rpath != p->rpath_orig) free(p->rpath); free(p->deps); + unmap_library(p); free(p); } tls_cnt = orig_tls_cnt; |