add real fdpic loading of shared libraries
previously, the normal ELF library loading code was used even for
fdpic, so only the kernel-loaded dynamic linker and main app could
benefit from separate placement of segments and shared text.
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index 4903dbd..2c822b6 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -502,6 +502,22 @@
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 @@
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 @@
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 @@
}
}
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 @@
* 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 @@
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 @@
}
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 @@
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;