diff options
Diffstat (limited to 'src/layout.c')
-rw-r--r-- | src/layout.c | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/src/layout.c b/src/layout.c new file mode 100644 index 0000000..859ab66 --- /dev/null +++ b/src/layout.c @@ -0,0 +1,656 @@ +/* Copyright (C) 2001, 2002, 2003, 2004, 2006, 2011 Red Hat, Inc. + Written by Jakub Jelinek <jakub@redhat.com>, 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include <alloca.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <assert.h> +#include "prelinktab.h" +#include "layout.h" + +#ifndef NDEBUG +# define DEBUG_LAYOUT +#endif + +#ifdef DEBUG_LAYOUT +void +print_ent (struct prelink_entry *e) +{ + printf ("%s: %08x %08x/%08x\n", + e->filename, (int)e->base, (int)e->end, (int)e->layend); +} + +void +print_list (struct prelink_entry *e) +{ + for (; e; e = e->next) + print_ent (e); + printf ("\n"); +} +#endif + +static int +find_arches (void **p, void *info) +{ + struct layout_libs *l = (struct layout_libs *) info; + struct prelink_entry *e = * (struct prelink_entry **) p; + int i; + + if (e->type == ET_DYN || e->type == ET_EXEC + || e->type == ET_CACHE_DYN || e->type == ET_CACHE_EXEC) + { + for (i = 0; i < l->nbinlibs; ++i) + if ((l->binlibs[i]->flags & (PCF_ELF64 | PCF_MACHINE)) + == (e->flags & (PCF_ELF64 | PCF_MACHINE))) + return 1; + + l->binlibs[l->nbinlibs++] = e; + } + + return 1; +} + +static int +find_libs (void **p, void *info) +{ + struct layout_libs *l = (struct layout_libs *) info; + struct prelink_entry *e = * (struct prelink_entry **) p; + + if ((e->flags & (PCF_ELF64 | PCF_MACHINE)) != l->flags) + return 1; + + if (e->type == ET_DYN || e->type == ET_EXEC + || e->type == ET_CACHE_DYN || e->type == ET_CACHE_EXEC) + l->binlibs[l->nbinlibs++] = e; + if (e->type == ET_DYN || e->type == ET_CACHE_DYN) + l->libs[l->nlibs++] = e; + if (force) + e->done = 0; + if (e->type == ET_CACHE_DYN || e->type == ET_CACHE_EXEC) + e->done = 2; + if (e->base & (l->max_page_size - 1)) + { + e->done = 0; + e->end -= e->base; + e->base = 0; + } + + return 1; +} + +static int +refs_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + int i; + + /* Dynamic linkers first. */ + if (! a->ndepends && b->ndepends) + return -1; + if (a->ndepends && ! b->ndepends) + return 1; + /* Most widely used libraries first. */ + if (a->refs > b->refs) + return -1; + if (a->refs < b->refs) + return 1; + /* Largest libraries first. */ + if (a->layend - a->base > b->layend - b->base) + return -1; + if (a->layend - a->base < b->layend - b->base) + return 1; + if (a->refs) + { + i = strcmp (a->soname, b->soname); + if (i) + return i; + } + return strcmp (a->filename, b->filename); +} + +static int +refs_rnd_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + int i, refs; + + /* Dynamic linkers first. */ + if (! a->ndepends && b->ndepends) + return -1; + if (a->ndepends && ! b->ndepends) + return 1; + /* Most widely used libraries first with some randomization. */ + refs = a->refs < b->refs ? a->refs : b->refs; + if (refs < 8) + i = 1; + else if (refs < 32) + i = 2; + else if (refs < 128) + i = 4; + else + i = 8; + if (a->refs > b->refs && a->refs - b->refs >= i) + return -1; + if (a->refs < b->refs && b->refs - a->refs >= i) + return 1; + if (a->u.tmp < b->u.tmp) + return -1; + if (a->u.tmp > b->u.tmp) + return 1; + /* Largest libraries first. */ + if (a->layend - a->base > b->layend - b->base) + return -1; + if (a->layend - a->base < b->layend - b->base) + return 1; + if (a->refs && b->refs) + { + i = strcmp (a->soname, b->soname); + if (i) + return i; + } + return strcmp (a->filename, b->filename); +} + +static int +addr_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + + if (! a->done) + return b->done ? 1 : 0; + else if (! b->done) + return -1; + if (a->base < b->base) + return -1; + else if (a->base > b->base) + return 1; + if (a->layend < b->layend) + return -1; + else if (a->layend > b->layend) + return 1; + return 0; +} + +int deps_cmp (const void *A, const void *B) +{ + struct prelink_entry *a = * (struct prelink_entry **) A; + struct prelink_entry *b = * (struct prelink_entry **) B; + + if (a->base < b->base) + return -1; + if (a->base > b->base) + return 1; + return 0; +} + +int +layout_libs (void) +{ + struct layout_libs l; + int arch, *arches, narches; + struct prelink_entry **plibs, **pbinlibs; + + memset (&l, 0, sizeof (l)); + l.libs = plibs = + (struct prelink_entry **) alloca (prelink_entry_count + * sizeof (struct prelink_entry *)); + l.binlibs = pbinlibs = + (struct prelink_entry **) alloca (prelink_entry_count + * sizeof (struct prelink_entry *)); + htab_traverse (prelink_filename_htab, find_arches, &l); + narches = l.nbinlibs; + arches = (int *) alloca (narches * sizeof (int)); + for (arch = 0; arch < narches; ++arch) + arches[arch] = l.binlibs[arch]->flags & (PCF_ELF64 | PCF_MACHINE); + + for (arch = 0; arch < narches; ++arch) + { + struct PLArch *plarch; + extern struct PLArch __start_pl_arch[], __stop_pl_arch[]; + int i, j, k, m, done, class; + GElf_Addr mmap_start, mmap_base, mmap_end, mmap_fin, max_page_size; + GElf_Addr base, size; + struct prelink_entry *list, *e, *fake, **deps; + struct prelink_entry fakeent; + int fakecnt; + int (*layout_libs_pre) (struct layout_libs *l); + int (*layout_libs_post) (struct layout_libs *l); + + for (plarch = __start_pl_arch; plarch < __stop_pl_arch; plarch++) + if (plarch->class == (arches[arch] & PCF_ELF64 ? ELFCLASS64 : ELFCLASS32) + && plarch->machine == (arches[arch] & PCF_MACHINE)) + break; + + if (plarch == __stop_pl_arch) + error (EXIT_FAILURE, 0, "%d-bit ELF e_machine %04x not supported", + (arches[arch] & PCF_ELF64) ? 64 : 32, arches[arch] & PCF_MACHINE); + + list = NULL; + fake = NULL; + fakecnt = 0; + memset (&l, 0, sizeof (l)); + l.flags = arches[arch]; + l.libs = plibs; + l.binlibs = pbinlibs; + max_page_size = plarch->max_page_size; + if (layout_page_size > max_page_size) + max_page_size = layout_page_size; + l.max_page_size = max_page_size; + htab_traverse (prelink_filename_htab, find_libs, &l); + + /* Make sure there is some room between libraries. */ + for (i = 0; i < l.nlibs; ++i) + l.libs[i]->layend = (l.libs[i]->end + 8192 + max_page_size - 1) + & ~(max_page_size - 1); + + if (plarch->layout_libs_init) + { + plarch->layout_libs_init (&l); + mmap_base = l.mmap_base; + mmap_end = l.mmap_end; + } + else + { + mmap_base = plarch->mmap_base; + mmap_end = plarch->mmap_end; + } + if (mmap_reg_start != ~(GElf_Addr) 0) + mmap_base = mmap_reg_start; + if (mmap_reg_end != ~(GElf_Addr) 0) + mmap_end = mmap_reg_end; + if (mmap_base >= mmap_end) + error (EXIT_FAILURE, 0, + "--mmap-region-start cannot be bigger than --mmap-region-end"); + if ((mmap_base | mmap_end) & (max_page_size - 1)) + error (EXIT_FAILURE, 0, "--layout-page-size too large"); + class = plarch->class; + /* The code below relies on having a VA slot as big as <mmap_base,mmap_end) + above mmap_end for -R. */ + if (mmap_end + (mmap_end - mmap_base) <= mmap_end) + random_base = 0; + layout_libs_pre = plarch->layout_libs_pre; + layout_libs_post = plarch->layout_libs_post; + + deps = (struct prelink_entry **) + alloca (l.nlibs * sizeof (struct prelink_entry *)); + + /* Now see which already prelinked libraries have to be + re-prelinked to avoid overlaps. */ + for (i = 0; i < l.nbinlibs; ++i) + { + for (j = 0, k = 0; j < l.binlibs[i]->ndepends; ++j) + if (l.binlibs[i]->depends[j]->type == ET_DYN + && l.binlibs[i]->depends[j]->done) + { + assert (k < l.nlibs); + deps[k++] = l.binlibs[i]->depends[j]; + } + if (k) + { + qsort (deps, k, sizeof (struct prelink_entry *), deps_cmp); + for (j = 1; j < k; ++j) + if (deps[j]->base < deps[j - 1]->end + && (deps[j]->type == ET_DYN + || deps[j - 1]->type == ET_DYN)) + { + if (deps[j - 1]->refs < deps[j]->refs) + --j; + deps[j]->done = 0; + --k; + memmove (deps + j, deps + j + 1, (k - j) * sizeof (*deps)); + if (j > 0) + --j; + } + } + } + + /* If layout_libs_init or the for cycle above cleared + done flags for some libraries, make sure all libraries + that depend on them are re-prelinked as well. */ + for (i = 0; i < l.nlibs; ++i) + if (l.libs[i]->done) + for (j = 0; j < l.libs[i]->ndepends; ++j) + if (l.libs[i]->depends[j]->done == 0) + { + l.libs[i]->done = 0; + break; + } + + /* Put the already prelinked libs into double linked list. */ + qsort (l.libs, l.nlibs, sizeof (struct prelink_entry *), addr_cmp); + for (i = 0; i < l.nlibs; ++i) + if (! l.libs[i]->done || l.libs[i]->layend >= mmap_base) + break; + j = 0; + if (i < l.nlibs && l.libs[i]->done) + { + if (l.libs[i]->base < mmap_base) + random_base = 0; + for (j = i + 1; j < l.nlibs; ++j) + { + if (! l.libs[j]->done || l.libs[j]->base >= mmap_end) + break; + + if (l.libs[j]->base < mmap_base || l.libs[j]->layend > mmap_end) + random_base = 0; + l.libs[j]->prev = l.libs[j - 1]; + l.libs[j - 1]->next = l.libs[j]; + } + list = l.libs[i]; + list->prev = l.libs[j - 1]; + while (j < l.nlibs && l.libs[j]->done) ++j; + } + + mmap_start = mmap_base; + mmap_fin = mmap_end; + done = 1; + if (random_base & 2) + { + mmap_start = seed; + if (mmap_start < mmap_base || mmap_start >= mmap_end) + mmap_start = mmap_base; + + mmap_start = (mmap_start + max_page_size - 1) & ~(max_page_size - 1); + } + else if (random_base) + { + int fd = open ("/dev/urandom", O_RDONLY); + + mmap_start = 0; + if (fd != -1) + { + GElf_Addr x; + + if (read (fd, &x, sizeof (x)) == sizeof (x)) + { + mmap_start = x % (mmap_end - mmap_base); + mmap_start += mmap_base; + } + + fsync (fd); + close (fd); + } + + if (! mmap_start) + { + mmap_start = ((mmap_end - mmap_base) >> 16) + * (time (NULL) & 0xffff); + mmap_start += mmap_base; + } + + seed = mmap_start; + mmap_start = (mmap_start + max_page_size - 1) & ~(max_page_size - 1); + } + if (random_base) + { + srandom (mmap_start >> 12); + for (i = 0; i < l.nlibs; ++i) + l.libs[i]->u.tmp = random (); + qsort (l.libs, l.nlibs, sizeof (struct prelink_entry *), refs_rnd_cmp); + } + else + qsort (l.libs, l.nlibs, sizeof (struct prelink_entry *), refs_cmp); + + if (verbose && l.nlibs > j) + { + printf ("Laying out %d libraries in virtual address space %0*llx-%0*llx\n", + l.nlibs - j, class == ELFCLASS32 ? 8 : 16, (long long) mmap_base, + class == ELFCLASS32 ? 8 : 16, (long long) mmap_end); + if (mmap_start != mmap_base) + printf ("Random base 0x%0*llx\n", class == ELFCLASS32 ? 8 : 16, + (long long) mmap_start); + } + + if (layout_libs_pre) + { + l.list = list; + l.mmap_base = mmap_base; + l.mmap_start = mmap_start; + l.mmap_end = mmap_end; + layout_libs_pre (&l); + list = l.list; + mmap_base = l.mmap_base; + mmap_start = l.mmap_start; + mmap_fin = l.mmap_fin; + mmap_end = l.mmap_end; + fake = l.fake; + fakecnt = l.fakecnt; + } + + if (mmap_start != mmap_base && list) + { + for (e = list; e != NULL; e = e->next) + { + if (e->base >= mmap_start) + break; + if (e->layend > mmap_start) + mmap_start = (e->layend + max_page_size - 1) + & ~(max_page_size - 1); + e->base += mmap_end - mmap_base; + e->end += mmap_end - mmap_base; + e->layend += mmap_end - mmap_base; + e->done |= 0x80; + } + + if (mmap_start < mmap_end) + { + if (e && e != list) + { + memset (&fakeent, 0, sizeof (fakeent)); + fakeent.u.tmp = -1; + fakeent.base = mmap_end; + fakeent.end = mmap_end; + fakeent.layend = mmap_end; + fake = &fakeent; + fakecnt = 1; + fakeent.prev = list->prev; + fakeent.next = list; + list->prev = fake; + fakeent.prev->next = fake; + list = e; + e->prev->next = NULL; + } + } + else + { + mmap_start = mmap_base; + for (e = list; e != NULL; e = e->next) + if (e->done & 0x80) + { + e->done &= ~0x80; + e->base -= mmap_end - mmap_base; + e->end -= mmap_end - mmap_base; + e->layend -= mmap_end - mmap_base; + } + } + } + + if (mmap_start != mmap_base) + { + done |= 0x80; + mmap_fin = mmap_end + (mmap_start - mmap_base); + } + + for (i = 0; i < l.nlibs; ++i) + l.libs[i]->u.tmp = -1; + m = -1; + + for (i = 0; i < l.nlibs; ++i) + if (! l.libs[i]->done) + { + if (conserve_memory) + { + /* If conserving virtual address space, only consider libraries + which ever appear together with this one. Otherwise consider + all libraries. */ + m = i; + for (j = 0; j < l.nbinlibs; ++j) + { + for (k = 0; k < l.binlibs[j]->ndepends; ++k) + if (l.binlibs[j]->depends[k] == l.libs[i]) + { + for (k = 0; k < l.binlibs[j]->ndepends; ++k) + l.binlibs[j]->depends[k]->u.tmp = m; + break; + } + } + for (j = 0; j < fakecnt; ++j) + fake[j].u.tmp = m; + } + + size = l.libs[i]->layend - l.libs[i]->base; + base = mmap_start; + for (e = list; e; e = e->next) + if (e->u.tmp == m) + { + if (base + size <= e->base) + goto found; + + if (base < e->layend) + base = e->layend; + } + + if (base + size > mmap_fin) + goto not_found; +found: + l.libs[i]->end += base - l.libs[i]->base; + l.libs[i]->base = base; + l.libs[i]->layend = base + size; + if (base >= mmap_end) + l.libs[i]->done = done; + else + l.libs[i]->done = 1; + if (list == NULL) + { + list = l.libs[i]; + list->prev = list; + } + else + { + if (e == NULL) + e = list->prev; + else + e = e->prev; + while (e != list && e->base > base) + e = e->prev; + if (e->base > base) + { + l.libs[i]->next = list; + l.libs[i]->prev = list->prev; + list->prev = l.libs[i]; + list = l.libs[i]; + } + else + { + l.libs[i]->next = e->next; + l.libs[i]->prev = e; + if (e->next) + e->next->prev = l.libs[i]; + else + list->prev = l.libs[i]; + e->next = l.libs[i]; + } + } +#ifdef DEBUG_LAYOUT + { + struct prelink_entry *last = list; + base = 0; + for (e = list; e; last = e, e = e->next) + { + if (e->base < base) + abort (); + base = e->base; + if ((e == list && e->prev->next != NULL) + || (e != list && e->prev->next != e)) + abort (); + } + if (list->prev != last) + abort (); + } +#endif + continue; + +not_found: + error (EXIT_FAILURE, 0, "Could not find virtual address slot for %s", + l.libs[i]->filename); + } + + if (layout_libs_post) + { + l.list = list; + layout_libs_post (&l); + } + + if (done & 0x80) + for (e = list; e != NULL; e = e->next) + if (e->done & 0x80) + { + e->done &= ~0x80; + e->base -= mmap_end - mmap_base; + e->end -= mmap_end - mmap_base; + e->layend -= mmap_base - mmap_base; + } + + if (verbose) + { + if (narches == 1) + printf ("Assigned virtual address space slots for libraries:\n"); + else + printf ("Assigned virtual address space slots for %d-bit %s ELF libraries:\n", + class == ELFCLASS32 ? 32 : 64, plarch->name); + + for (i = 0; i < l.nlibs; ++i) + if (l.libs[i]->done >= 1) + printf ("%-60s %0*llx-%0*llx\n", l.libs[i]->filename, + class == ELFCLASS32 ? 8 : 16, (long long) l.libs[i]->base, + class == ELFCLASS32 ? 8 : 16, (long long) l.libs[i]->end); + } + +#ifdef DEBUG_LAYOUT + for (i = 0; i < l.nbinlibs; ++i) + { + for (j = 0; j < l.binlibs[i]->ndepends; ++j) + if ((l.binlibs[i]->depends[j]->type != ET_DYN + && l.binlibs[i]->depends[j]->type != ET_CACHE_DYN) + || l.binlibs[i]->depends[j]->done == 0) + break; + if (j < l.binlibs[i]->ndepends) + continue; + memcpy (deps, l.binlibs[i]->depends, + l.binlibs[i]->ndepends * sizeof (struct prelink_entry *)); + qsort (deps, l.binlibs[i]->ndepends, sizeof (struct prelink_entry *), + deps_cmp); + for (j = 1; j < l.binlibs[i]->ndepends; ++j) + if (deps[j]->base + < ((deps[j - 1]->end + max_page_size - 1) + & ~(max_page_size - 1)) + && (deps[j]->type == ET_DYN || deps[j - 1]->type == ET_DYN)) + abort (); + } +#endif + } + + return 0; +} |