/* Copyright (C) 2001, 2002, 2003, 2004, 2006, 2011 Red Hat, Inc. Written by Jakub Jelinek , 2001. * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #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 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; }