Blame elf/dl-map-segments.h

Packit 6c4009
/* Map in a shared object's segments.  Generic version.
Packit 6c4009
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <dl-load.h>
Packit 6c4009
Packit 6c4009
/* This implementation assumes (as does the corresponding implementation
Packit 6c4009
   of _dl_unmap_segments, in dl-unmap-segments.h) that shared objects
Packit 6c4009
   are always laid out with all segments contiguous (or with gaps
Packit 6c4009
   between them small enough that it's preferable to reserve all whole
Packit 6c4009
   pages inside the gaps with PROT_NONE mappings rather than permitting
Packit 6c4009
   other use of those parts of the address space).  */
Packit 6c4009
Packit 6c4009
static __always_inline const char *
Packit 6c4009
_dl_map_segments (struct link_map *l, int fd,
Packit 6c4009
                  const ElfW(Ehdr) *header, int type,
Packit 6c4009
                  const struct loadcmd loadcmds[], size_t nloadcmds,
Packit 6c4009
                  const size_t maplength, bool has_holes,
Packit 6c4009
                  struct link_map *loader)
Packit 6c4009
{
Packit 6c4009
  const struct loadcmd *c = loadcmds;
Packit 6c4009
Packit 6c4009
  if (__glibc_likely (type == ET_DYN))
Packit 6c4009
    {
Packit 6c4009
      /* This is a position-independent shared object.  We can let the
Packit 6c4009
         kernel map it anywhere it likes, but we must have space for all
Packit 6c4009
         the segments in their specified positions relative to the first.
Packit 6c4009
         So we map the first segment without MAP_FIXED, but with its
Packit 6c4009
         extent increased to cover all the segments.  Then we remove
Packit 6c4009
         access from excess portion, and there is known sufficient space
Packit 6c4009
         there to remap from the later segments.
Packit 6c4009
Packit 6c4009
         As a refinement, sometimes we have an address that we would
Packit 6c4009
         prefer to map such objects at; but this is only a preference,
Packit 6c4009
         the OS can do whatever it likes. */
Packit 6c4009
      ElfW(Addr) mappref
Packit 6c4009
        = (ELF_PREFERRED_ADDRESS (loader, maplength,
Packit 6c4009
                                  c->mapstart & GLRO(dl_use_load_bias))
Packit 6c4009
           - MAP_BASE_ADDR (l));
Packit 6c4009
Packit 6c4009
      /* Remember which part of the address space this object uses.  */
Packit 6c4009
      l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength,
Packit 6c4009
                                            c->prot,
Packit 6c4009
                                            MAP_COPY|MAP_FILE,
Packit 6c4009
                                            fd, c->mapoff);
Packit 6c4009
      if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
Packit 6c4009
        return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
Packit 6c4009
Packit 6c4009
      l->l_map_end = l->l_map_start + maplength;
Packit 6c4009
      l->l_addr = l->l_map_start - c->mapstart;
Packit 6c4009
Packit 6c4009
      if (has_holes)
Packit 6c4009
        {
Packit 6c4009
          /* Change protection on the excess portion to disallow all access;
Packit 6c4009
             the portions we do not remap later will be inaccessible as if
Packit 6c4009
             unallocated.  Then jump into the normal segment-mapping loop to
Packit 6c4009
             handle the portion of the segment past the end of the file
Packit 6c4009
             mapping.  */
Packit 6c4009
          if (__glibc_unlikely
Packit 6c4009
              (__mprotect ((caddr_t) (l->l_addr + c->mapend),
Packit 6c4009
                           loadcmds[nloadcmds - 1].mapstart - c->mapend,
Packit 6c4009
                           PROT_NONE) < 0))
Packit 6c4009
            return DL_MAP_SEGMENTS_ERROR_MPROTECT;
Packit 6c4009
        }
Packit 6c4009
Packit 6c4009
      l->l_contiguous = 1;
Packit 6c4009
Packit 6c4009
      goto postmap;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Remember which part of the address space this object uses.  */
Packit 6c4009
  l->l_map_start = c->mapstart + l->l_addr;
Packit 6c4009
  l->l_map_end = l->l_map_start + maplength;
Packit 6c4009
  l->l_contiguous = !has_holes;
Packit 6c4009
Packit 6c4009
  while (c < &loadcmds[nloadcmds])
Packit 6c4009
    {
Packit 6c4009
      if (c->mapend > c->mapstart
Packit 6c4009
          /* Map the segment contents from the file.  */
Packit 6c4009
          && (__mmap ((void *) (l->l_addr + c->mapstart),
Packit 6c4009
                      c->mapend - c->mapstart, c->prot,
Packit 6c4009
                      MAP_FIXED|MAP_COPY|MAP_FILE,
Packit 6c4009
                      fd, c->mapoff)
Packit 6c4009
              == MAP_FAILED))
Packit 6c4009
        return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
Packit 6c4009
Packit 6c4009
    postmap:
Packit 6c4009
      _dl_postprocess_loadcmd (l, header, c);
Packit 6c4009
Packit 6c4009
      if (c->allocend > c->dataend)
Packit 6c4009
        {
Packit 6c4009
          /* Extra zero pages should appear at the end of this segment,
Packit 6c4009
             after the data mapped from the file.   */
Packit 6c4009
          ElfW(Addr) zero, zeroend, zeropage;
Packit 6c4009
Packit 6c4009
          zero = l->l_addr + c->dataend;
Packit 6c4009
          zeroend = l->l_addr + c->allocend;
Packit 6c4009
          zeropage = ((zero + GLRO(dl_pagesize) - 1)
Packit 6c4009
                      & ~(GLRO(dl_pagesize) - 1));
Packit 6c4009
Packit 6c4009
          if (zeroend < zeropage)
Packit 6c4009
            /* All the extra data is in the last page of the segment.
Packit 6c4009
               We can just zero it.  */
Packit 6c4009
            zeropage = zeroend;
Packit 6c4009
Packit 6c4009
          if (zeropage > zero)
Packit 6c4009
            {
Packit 6c4009
              /* Zero the final part of the last page of the segment.  */
Packit 6c4009
              if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
Packit 6c4009
                {
Packit 6c4009
                  /* Dag nab it.  */
Packit 6c4009
                  if (__mprotect ((caddr_t) (zero
Packit 6c4009
                                             & ~(GLRO(dl_pagesize) - 1)),
Packit 6c4009
                                  GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0)
Packit 6c4009
                    return DL_MAP_SEGMENTS_ERROR_MPROTECT;
Packit 6c4009
                }
Packit 6c4009
              memset ((void *) zero, '\0', zeropage - zero);
Packit 6c4009
              if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
Packit 6c4009
                __mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)),
Packit 6c4009
                            GLRO(dl_pagesize), c->prot);
Packit 6c4009
            }
Packit 6c4009
Packit 6c4009
          if (zeroend > zeropage)
Packit 6c4009
            {
Packit 6c4009
              /* Map the remaining zero pages in from the zero fill FD.  */
Packit 6c4009
              caddr_t mapat;
Packit 6c4009
              mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
Packit 6c4009
                              c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
Packit 6c4009
                              -1, 0);
Packit 6c4009
              if (__glibc_unlikely (mapat == MAP_FAILED))
Packit 6c4009
                return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL;
Packit 6c4009
            }
Packit 6c4009
        }
Packit 6c4009
Packit 6c4009
      ++c;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Notify ELF_PREFERRED_ADDRESS that we have to load this one
Packit 6c4009
     fixed.  */
Packit 6c4009
  ELF_FIXED_ADDRESS (loader, c->mapstart);
Packit 6c4009
Packit 6c4009
  return NULL;
Packit 6c4009
}