]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Factor mmap/munmap of PT_LOAD segments out of _dl_map_object_from_fd et al.
authorRoland McGrath <roland@hack.frob.com>
Thu, 3 Apr 2014 17:47:14 +0000 (10:47 -0700)
committerRoland McGrath <roland@hack.frob.com>
Thu, 3 Apr 2014 17:47:14 +0000 (10:47 -0700)
15 files changed:
ChangeLog
elf/dl-close.c
elf/dl-fptr.c
elf/dl-load.c
elf/dl-load.h [new file with mode: 0644]
elf/dl-map-segments.h [new file with mode: 0644]
elf/dl-unmap-segments.h [new file with mode: 0644]
ports/ChangeLog.hppa
ports/sysdeps/hppa/dl-fptr.c
sysdeps/aarch64/tlsdesc.c
sysdeps/arm/tlsdesc.c
sysdeps/generic/ldsodefs.h
sysdeps/i386/tlsdesc.c
sysdeps/tile/dl-runtime.c
sysdeps/x86_64/tlsdesc.c

index e36b689d29eb1327b48b2f3e88bfcf870cce8caf..7a1c10620ae2b3fa19ef9d803b6d658c7847619e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2014-04-03  Roland McGrath  <roland@hack.frob.com>
+
+       * elf/dl-unmap-segments.h: New file.
+       * sysdeps/generic/ldsodefs.h
+       (DL_UNMAP): Use _dl_unmap_segments in place of __munmap.
+       * elf/dl-close.c: Include <dl-unmap-segments.h>.
+       * elf/dl-fptr.c: Likewise.
+       (_dl_unmap): Use _dl_unmap_segments in place of __munmap.
+       * sysdeps/aarch64/tlsdesc.c: Likewise.
+       * sysdeps/arm/tlsdesc.c: Likewise.
+       * sysdeps/i386/tlsdesc.c: Likewise.
+       * sysdeps/tile/dl-runtime.c: Likewise.
+       * sysdeps/x86_64/tlsdesc.c: Likewise.
+       * elf/dl-load.h: New file.
+       * elf/dl-load.c: Include it.
+       (MAP_FILE, MAP_COPY, MAP_BASE_ADDR):
+       Macros moved to dl-load.h.
+       (ELF_PREFERRED_ADDRESS_DATA, ELF_PREFERRED_ADDRESS): Likewise.
+       (_dl_map_object_from_fd): Type 'struct loadcmd' moved to dl-load.h.
+       Use _dl_unmap_segments in place of __munmap.
+       Break out segment-mapping loop into ...
+       * elf/dl-map-segments.h (_dl_map_segments): ... here, in new file.
+
 2014-04-03  Will Newton  <will.newton@linaro.org>
 
        * elf/dl-lookup.c (do_lookup_x): Remove comment
index f36ea0f779a2d8a23eaf05819fd44279d9bb628e..d74d55eb87f3fca37a003c06065d711897e91300 100644 (file)
@@ -33,6 +33,8 @@
 #include <tls.h>
 #include <stap-probe.h>
 
+#include <dl-unmap-segments.h>
+
 
 /* Type of the constructor functions.  */
 typedef void (*fini_t) (void);
index 40cb40a3f1143b0ca76d95ab7691ed1e605341c6..f1478e26bbc230f8041f6281d38ec3ff76591969 100644 (file)
@@ -25,6 +25,7 @@
 #include <ldsodefs.h>
 #include <elf/dynamic-link.h>
 #include <dl-fptr.h>
+#include <dl-unmap-segments.h>
 #include <atomic.h>
 
 #ifndef ELF_MACHINE_BOOT_FPTR_TABLE_LEN
@@ -269,8 +270,7 @@ _dl_unmap (struct link_map *map)
   struct fdesc *head = NULL, *tail = NULL;
   size_t i;
 
-  __munmap ((void *) map->l_map_start,
-           map->l_map_end - map->l_map_start);
+  _dl_unmap_segments (map);
 
   if (ftab == NULL)
     return;
index 16b101af0cf9be4ed20dee01fb7b4827adf3ccdc..cfa7f2525fc6543fc3944f207dd29d5cc7102894 100644 (file)
 #include <stap-probe.h>
 
 #include <dl-dst.h>
-
-/* On some systems, no flag bits are given to specify file mapping.  */
-#ifndef MAP_FILE
-# define MAP_FILE      0
-#endif
-
-/* The right way to map in the shared library files is MAP_COPY, which
-   makes a virtual copy of the data at the time of the mmap call; this
-   guarantees the mapped pages will be consistent even if the file is
-   overwritten.  Some losing VM systems like Linux's lack MAP_COPY.  All we
-   get is MAP_PRIVATE, which copies each page when it is modified; this
-   means if the file is overwritten, we may at some point get some pages
-   from the new version after starting with pages from the old version.
-
-   To make up for the lack and avoid the overwriting problem,
-   what Linux does have is MAP_DENYWRITE.  This prevents anyone
-   from modifying the file while we have it mapped.  */
-#ifndef MAP_COPY
-# ifdef MAP_DENYWRITE
-#  define MAP_COPY     (MAP_PRIVATE | MAP_DENYWRITE)
-# else
-#  define MAP_COPY     MAP_PRIVATE
-# endif
-#endif
-
-/* Some systems link their relocatable objects for another base address
-   than 0.  We want to know the base address for these such that we can
-   subtract this address from the segment addresses during mapping.
-   This results in a more efficient address space usage.  Defaults to
-   zero for almost all systems.  */
-#ifndef MAP_BASE_ADDR
-# define MAP_BASE_ADDR(l)      0
-#endif
+#include <dl-load.h>
+#include <dl-map-segments.h>
+#include <dl-unmap-segments.h>
 
 
 #include <endian.h>
 
 #define STRING(x) __STRING (x)
 
-/* Handle situations where we have a preferred location in memory for
-   the shared objects.  */
-#ifdef ELF_PREFERRED_ADDRESS_DATA
-ELF_PREFERRED_ADDRESS_DATA;
-#endif
-#ifndef ELF_PREFERRED_ADDRESS
-# define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)
-#endif
-#ifndef ELF_FIXED_ADDRESS
-# define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)
-#endif
-
 
 int __stack_prot attribute_hidden attribute_relro
 #if _STACK_GROWS_DOWN && defined PROT_GROWSDOWN
@@ -1093,12 +1051,7 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
 
   {
     /* Scan the program header table, collecting its load commands.  */
-    struct loadcmd
-      {
-       ElfW(Addr) mapstart, mapend, dataend, allocend;
-       ElfW(Off) mapoff;
-       int prot;
-      } loadcmds[l->l_phnum], *c;
+    struct loadcmd loadcmds[l->l_phnum];
     size_t nloadcmds = 0;
     bool has_holes = false;
 
@@ -1137,7 +1090,7 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp,
              goto call_lose;
            }
 
-         c = &loadcmds[nloadcmds++];
+         struct loadcmd *c = &loadcmds[nloadcmds++];
          c->mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1);
          c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1)
                       & ~(GLRO(dl_pagesize) - 1));
@@ -1263,154 +1216,26 @@ cannot allocate TLS data structures for initial thread");
        goto call_lose;
       }
 
-    /* Now process the load commands and map segments into memory.  */
-    c = loadcmds;
-
-    /* Length of the sections to be loaded.  */
-    maplength = loadcmds[nloadcmds - 1].allocend - c->mapstart;
-
-    if (__glibc_likely (type == ET_DYN))
-      {
-       /* This is a position-independent shared object.  We can let the
-          kernel map it anywhere it likes, but we must have space for all
-          the segments in their specified positions relative to the first.
-          So we map the first segment without MAP_FIXED, but with its
-          extent increased to cover all the segments.  Then we remove
-          access from excess portion, and there is known sufficient space
-          there to remap from the later segments.
-
-          As a refinement, sometimes we have an address that we would
-          prefer to map such objects at; but this is only a preference,
-          the OS can do whatever it likes. */
-       ElfW(Addr) mappref;
-       mappref = (ELF_PREFERRED_ADDRESS (loader, maplength,
-                                         c->mapstart & GLRO(dl_use_load_bias))
-                  - MAP_BASE_ADDR (l));
-
-       /* Remember which part of the address space this object uses.  */
-       l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength,
-                                             c->prot,
-                                             MAP_COPY|MAP_FILE,
-                                             fd, c->mapoff);
-       if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
-         {
-         map_error:
-           errstring = N_("failed to map segment from shared object");
-           goto call_lose_errno;
-         }
-
-       l->l_map_end = l->l_map_start + maplength;
-       l->l_addr = l->l_map_start - c->mapstart;
-
-       if (has_holes)
-         /* Change protection on the excess portion to disallow all access;
-            the portions we do not remap later will be inaccessible as if
-            unallocated.  Then jump into the normal segment-mapping loop to
-            handle the portion of the segment past the end of the file
-            mapping.  */
-         __mprotect ((caddr_t) (l->l_addr + c->mapend),
-                     loadcmds[nloadcmds - 1].mapstart - c->mapend,
-                     PROT_NONE);
-
-       l->l_contiguous = 1;
-
-       goto postmap;
-      }
-
-    /* This object is loaded at a fixed address.  This must never
-       happen for objects loaded with dlopen().  */
-    if (__glibc_unlikely ((mode & __RTLD_OPENEXEC) == 0))
+    if (__glibc_unlikely (type != ET_DYN)
+       && __glibc_unlikely ((mode & __RTLD_OPENEXEC) == 0))
       {
+       /* This object is loaded at a fixed address.  This must never
+          happen for objects loaded with dlopen.  */
        errstring = N_("cannot dynamically load executable");
        goto call_lose;
       }
 
-    /* Notify ELF_PREFERRED_ADDRESS that we have to load this one
-       fixed.  */
-    ELF_FIXED_ADDRESS (loader, c->mapstart);
-
-
-    /* Remember which part of the address space this object uses.  */
-    l->l_map_start = c->mapstart + l->l_addr;
-    l->l_map_end = l->l_map_start + maplength;
-    l->l_contiguous = !has_holes;
-
-    while (c < &loadcmds[nloadcmds])
-      {
-       if (c->mapend > c->mapstart
-           /* Map the segment contents from the file.  */
-           && (__mmap ((void *) (l->l_addr + c->mapstart),
-                       c->mapend - c->mapstart, c->prot,
-                       MAP_FIXED|MAP_COPY|MAP_FILE,
-                       fd, c->mapoff)
-               == MAP_FAILED))
-         goto map_error;
-
-      postmap:
-       if (c->prot & PROT_EXEC)
-         l->l_text_end = l->l_addr + c->mapend;
-
-       if (l->l_phdr == 0
-           && c->mapoff <= header->e_phoff
-           && ((size_t) (c->mapend - c->mapstart + c->mapoff)
-               >= header->e_phoff + header->e_phnum * sizeof (ElfW(Phdr))))
-         /* Found the program header in this segment.  */
-         l->l_phdr = (void *) (uintptr_t) (c->mapstart + header->e_phoff
-                                           - c->mapoff);
-
-       if (c->allocend > c->dataend)
-         {
-           /* Extra zero pages should appear at the end of this segment,
-              after the data mapped from the file.   */
-           ElfW(Addr) zero, zeroend, zeropage;
-
-           zero = l->l_addr + c->dataend;
-           zeroend = l->l_addr + c->allocend;
-           zeropage = ((zero + GLRO(dl_pagesize) - 1)
-                       & ~(GLRO(dl_pagesize) - 1));
-
-           if (zeroend < zeropage)
-             /* All the extra data is in the last page of the segment.
-                We can just zero it.  */
-             zeropage = zeroend;
-
-           if (zeropage > zero)
-             {
-               /* Zero the final part of the last page of the segment.  */
-               if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
-                 {
-                   /* Dag nab it.  */
-                   if (__mprotect ((caddr_t) (zero
-                                              & ~(GLRO(dl_pagesize) - 1)),
-                                   GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0)
-                     {
-                       errstring = N_("cannot change memory protections");
-                       goto call_lose_errno;
-                     }
-                 }
-               memset ((void *) zero, '\0', zeropage - zero);
-               if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
-                 __mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)),
-                             GLRO(dl_pagesize), c->prot);
-             }
-
-           if (zeroend > zeropage)
-             {
-               /* Map the remaining zero pages in from the zero fill FD.  */
-               caddr_t mapat;
-               mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
-                               c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
-                               -1, 0);
-               if (__glibc_unlikely (mapat == MAP_FAILED))
-                 {
-                   errstring = N_("cannot map zero-fill pages");
-                   goto call_lose_errno;
-                 }
-             }
-         }
-
-       ++c;
-      }
+    /* Length of the sections to be loaded.  */
+    maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart;
+
+    /* Now process the load commands and map segments into memory.
+       This is responsible for filling in:
+       l_map_start, l_map_end, l_addr, l_contiguous, l_text_end, l_phdr
+     */
+    errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds,
+                                  maplength, has_holes, loader);
+    if (__glibc_unlikely (errstring != NULL))
+      goto call_lose;
   }
 
   if (l->l_ld == 0)
@@ -1432,7 +1257,7 @@ cannot allocate TLS data structures for initial thread");
       && (mode & __RTLD_DLOPEN))
     {
       /* We are not supposed to load this object.  Free all resources.  */
-      __munmap ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+      _dl_unmap_segments (l);
 
       if (!l->l_libname->dont_free)
        free (l->l_libname);
@@ -1738,8 +1563,8 @@ open_verify (const char *name, struct filebuf *fbp, struct link_map *loader,
       assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
       /* Read in the header.  */
       do
-        {
-          ssize_t retlen = __libc_read (fd, fbp->buf + fbp->len,
+       {
+         ssize_t retlen = __libc_read (fd, fbp->buf + fbp->len,
                                        sizeof (fbp->buf) - fbp->len);
          if (retlen <= 0)
            break;
diff --git a/elf/dl-load.h b/elf/dl-load.h
new file mode 100644 (file)
index 0000000..602840e
--- /dev/null
@@ -0,0 +1,135 @@
+/* Map in a shared object's segments from the file.
+   Copyright (C) 1995-2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _DL_LOAD_H
+#define _DL_LOAD_H     1
+
+#include <link.h>
+#include <sys/mman.h>
+
+
+/* On some systems, no flag bits are given to specify file mapping.  */
+#ifndef MAP_FILE
+# define MAP_FILE       0
+#endif
+
+/* The right way to map in the shared library files is MAP_COPY, which
+   makes a virtual copy of the data at the time of the mmap call; this
+   guarantees the mapped pages will be consistent even if the file is
+   overwritten.  Some losing VM systems like Linux's lack MAP_COPY.  All we
+   get is MAP_PRIVATE, which copies each page when it is modified; this
+   means if the file is overwritten, we may at some point get some pages
+   from the new version after starting with pages from the old version.
+
+   To make up for the lack and avoid the overwriting problem,
+   what Linux does have is MAP_DENYWRITE.  This prevents anyone
+   from modifying the file while we have it mapped.  */
+#ifndef MAP_COPY
+# ifdef MAP_DENYWRITE
+#  define MAP_COPY      (MAP_PRIVATE | MAP_DENYWRITE)
+# else
+#  define MAP_COPY      MAP_PRIVATE
+# endif
+#endif
+
+/* Some systems link their relocatable objects for another base address
+   than 0.  We want to know the base address for these such that we can
+   subtract this address from the segment addresses during mapping.
+   This results in a more efficient address space usage.  Defaults to
+   zero for almost all systems.  */
+#ifndef MAP_BASE_ADDR
+# define MAP_BASE_ADDR(l)       0
+#endif
+
+
+/* Handle situations where we have a preferred location in memory for
+   the shared objects.  */
+#ifdef ELF_PREFERRED_ADDRESS_DATA
+ELF_PREFERRED_ADDRESS_DATA;
+#endif
+#ifndef ELF_PREFERRED_ADDRESS
+# define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)
+#endif
+#ifndef ELF_FIXED_ADDRESS
+# define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)
+#endif
+
+
+/* This structure describes one PT_LOAD command.
+   Its details have been expanded out and converted.  */
+struct loadcmd
+{
+  ElfW(Addr) mapstart, mapend, dataend, allocend;
+  ElfW(Off) mapoff;
+  int prot;                             /* PROT_* bits.  */
+};
+
+
+/* This is a subroutine of _dl_map_segments.  It should be called for each
+   load command, some time after L->l_addr has been set correctly.  It is
+   responsible for setting up the l_text_end and l_phdr fields.  */
+static void __always_inline
+_dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
+                         const struct loadcmd *c)
+{
+  if (c->prot & PROT_EXEC)
+    l->l_text_end = l->l_addr + c->mapend;
+
+  if (l->l_phdr == 0
+      && c->mapoff <= header->e_phoff
+      && ((size_t) (c->mapend - c->mapstart + c->mapoff)
+          >= header->e_phoff + header->e_phnum * sizeof (ElfW(Phdr))))
+    /* Found the program header in this segment.  */
+    l->l_phdr = (void *) (uintptr_t) (c->mapstart + header->e_phoff
+                                      - c->mapoff);
+}
+
+
+/* This is a subroutine of _dl_map_object_from_fd.  It is responsible
+   for filling in several fields in *L: l_map_start, l_map_end, l_addr,
+   l_contiguous, l_text_end, l_phdr.  On successful return, all the
+   segments are mapped (or copied, or whatever) from the file into their
+   final places in the address space, with the correct page permissions,
+   and any bss-like regions already zeroed.  It returns a null pointer
+   on success, or an error message string (to be translated) on error
+   (having also set errno).
+
+   The file <dl-map-segments.h> defines this function.  The canonical
+   implementation in elf/dl-map-segments.h might be replaced by a sysdeps
+   version.  */
+static const char *_dl_map_segments (struct link_map *l, int fd,
+                                     const ElfW(Ehdr) *header, int type,
+                                     const struct loadcmd loadcmds[],
+                                     size_t nloadcmds,
+                                     const size_t maplength,
+                                     bool has_holes,
+                                     struct link_map *loader);
+
+/* All the error message strings _dl_map_segments might return are
+   listed here so that different implementations in different sysdeps
+   dl-map-segments.h files all use consistent strings that are
+   guaranteed to have translations.  */
+#define DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT \
+  N_("failed to map segment from shared object")
+#define DL_MAP_SEGMENTS_ERROR_MPROTECT \
+  N_("cannot change memory protections")
+#define DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL \
+  N_("cannot map zero-fill pages")
+
+
+#endif /* dl-load.h */
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
new file mode 100644 (file)
index 0000000..71671cd
--- /dev/null
@@ -0,0 +1,153 @@
+/* Map in a shared object's segments.  Generic version.
+   Copyright (C) 1995-2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <dl-load.h>
+
+/* This implementation assumes (as does the corresponding implementation
+   of _dl_unmap_segments, in dl-unmap-segments.h) that shared objects
+   are always laid out with all segments contiguous (or with gaps
+   between them small enough that it's preferable to reserve all whole
+   pages inside the gaps with PROT_NONE mappings rather than permitting
+   other use of those parts of the address space).  */
+
+static __always_inline const char *
+_dl_map_segments (struct link_map *l, int fd,
+                  const ElfW(Ehdr) *header, int type,
+                  const struct loadcmd loadcmds[], size_t nloadcmds,
+                  const size_t maplength, bool has_holes,
+                  struct link_map *loader)
+{
+  const struct loadcmd *c = loadcmds;
+
+  if (__glibc_likely (type == ET_DYN))
+    {
+      /* This is a position-independent shared object.  We can let the
+         kernel map it anywhere it likes, but we must have space for all
+         the segments in their specified positions relative to the first.
+         So we map the first segment without MAP_FIXED, but with its
+         extent increased to cover all the segments.  Then we remove
+         access from excess portion, and there is known sufficient space
+         there to remap from the later segments.
+
+         As a refinement, sometimes we have an address that we would
+         prefer to map such objects at; but this is only a preference,
+         the OS can do whatever it likes. */
+      ElfW(Addr) mappref
+        = (ELF_PREFERRED_ADDRESS (loader, maplength,
+                                  c->mapstart & GLRO(dl_use_load_bias))
+           - MAP_BASE_ADDR (l));
+
+      /* Remember which part of the address space this object uses.  */
+      l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength,
+                                            c->prot,
+                                            MAP_COPY|MAP_FILE,
+                                            fd, c->mapoff);
+      if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
+        return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
+
+      l->l_map_end = l->l_map_start + maplength;
+      l->l_addr = l->l_map_start - c->mapstart;
+
+      if (has_holes)
+        /* Change protection on the excess portion to disallow all access;
+           the portions we do not remap later will be inaccessible as if
+           unallocated.  Then jump into the normal segment-mapping loop to
+           handle the portion of the segment past the end of the file
+           mapping.  */
+        __mprotect ((caddr_t) (l->l_addr + c->mapend),
+                    loadcmds[nloadcmds - 1].mapstart - c->mapend,
+                    PROT_NONE);
+
+      l->l_contiguous = 1;
+
+      goto postmap;
+    }
+
+  /* Remember which part of the address space this object uses.  */
+  l->l_map_start = c->mapstart + l->l_addr;
+  l->l_map_end = l->l_map_start + maplength;
+  l->l_contiguous = !has_holes;
+
+  while (c < &loadcmds[nloadcmds])
+    {
+      if (c->mapend > c->mapstart
+          /* Map the segment contents from the file.  */
+          && (__mmap ((void *) (l->l_addr + c->mapstart),
+                      c->mapend - c->mapstart, c->prot,
+                      MAP_FIXED|MAP_COPY|MAP_FILE,
+                      fd, c->mapoff)
+              == MAP_FAILED))
+        return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
+
+    postmap:
+      _dl_postprocess_loadcmd (l, header, c);
+
+      if (c->allocend > c->dataend)
+        {
+          /* Extra zero pages should appear at the end of this segment,
+             after the data mapped from the file.   */
+          ElfW(Addr) zero, zeroend, zeropage;
+
+          zero = l->l_addr + c->dataend;
+          zeroend = l->l_addr + c->allocend;
+          zeropage = ((zero + GLRO(dl_pagesize) - 1)
+                      & ~(GLRO(dl_pagesize) - 1));
+
+          if (zeroend < zeropage)
+            /* All the extra data is in the last page of the segment.
+               We can just zero it.  */
+            zeropage = zeroend;
+
+          if (zeropage > zero)
+            {
+              /* Zero the final part of the last page of the segment.  */
+              if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
+                {
+                  /* Dag nab it.  */
+                  if (__mprotect ((caddr_t) (zero
+                                             & ~(GLRO(dl_pagesize) - 1)),
+                                  GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0)
+                    return DL_MAP_SEGMENTS_ERROR_MPROTECT;
+                }
+              memset ((void *) zero, '\0', zeropage - zero);
+              if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
+                __mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)),
+                            GLRO(dl_pagesize), c->prot);
+            }
+
+          if (zeroend > zeropage)
+            {
+              /* Map the remaining zero pages in from the zero fill FD.  */
+              caddr_t mapat;
+              mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
+                              c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
+                              -1, 0);
+              if (__glibc_unlikely (mapat == MAP_FAILED))
+                return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL;
+            }
+        }
+
+      ++c;
+    }
+
+  /* Notify ELF_PREFERRED_ADDRESS that we have to load this one
+     fixed.  */
+  ELF_FIXED_ADDRESS (loader, c->mapstart);
+
+  return NULL;
+}
diff --git a/elf/dl-unmap-segments.h b/elf/dl-unmap-segments.h
new file mode 100644 (file)
index 0000000..ce9c3fe
--- /dev/null
@@ -0,0 +1,35 @@
+/* Unmap a shared object's segments.  Generic version.
+   Copyright (C) 2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _DL_UNMAP_SEGMENTS_H
+#define _DL_UNMAP_SEGMENTS_H   1
+
+#include <link.h>
+#include <sys/mman.h>
+
+/* _dl_map_segments ensures that any whole pages in gaps between segments
+   are filled in with PROT_NONE mappings.  So we can just unmap the whole
+   range in one fell swoop.  */
+
+static __always_inline void
+_dl_unmap_segments (struct link_map *l)
+{
+  __munmap ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+}
+
+#endif  /* dl-unmap-segments.h */
index 61aeabc30bfb93759c0d293bd7a74535192bd75f..4433ad5ca43c870f1918116735336fec07e9c801 100644 (file)
@@ -1,3 +1,8 @@
+2014-03-18  Roland McGrath  <roland@hack.frob.com>
+
+       * sysdeps/hppa/dl-fptr.c: Include <dl-unmap-segments.h>.
+       (_dl_unmap): Use _dl_unmap_segments in place of __munmap.
+
 2014-02-28  Roland McGrath  <roland@hack.frob.com>
 
        * sysdeps/hppa/nptl/tls.h (TLS_TCB_AT_TP): New macro.
index 7404f5ff915c6bf4f0cca8947b1fb7ee3436c292..f975664dd65034627b4f322590fa89f0855b77d1 100644 (file)
@@ -26,6 +26,7 @@
 #include <ldsodefs.h>
 #include <elf/dynamic-link.h>
 #include <dl-fptr.h>
+#include <dl-unmap-segments.h>
 #include <atomic.h>
 
 #ifndef ELF_MACHINE_BOOT_FPTR_TABLE_LEN
@@ -284,8 +285,7 @@ _dl_unmap (struct link_map *map)
   struct fdesc *head = NULL, *tail = NULL;
   size_t i;
 
-  __munmap ((void *) map->l_map_start,
-           map->l_map_end - map->l_map_start);
+  _dl_unmap_segments (map);
 
   if (ftab == NULL)
     return;
index 0921230676da9d7ef90213ea54c120b733e54d26..1b4181956c360929bc15a428634a040dc8fe36ec 100644 (file)
@@ -23,6 +23,7 @@
 #include <elf/dynamic-link.h>
 #include <tls.h>
 #include <dl-tlsdesc.h>
+#include <dl-unmap-segments.h>
 #include <tlsdeschtab.h>
 
 /* The following functions take an entry_check_offset argument.  It's
@@ -144,8 +145,7 @@ void
 internal_function
 _dl_unmap (struct link_map *map)
 {
-  __munmap ((void *) (map)->l_map_start,
-           (map)->l_map_end - (map)->l_map_start);
+  _dl_unmap_segments (map);
 
 #if SHARED
   if (map->l_mach.tlsdesc_table)
index fc754d6c42876b5cfb743563100243922ff18a3d..e52034ded6fa26a72d91691c9fc37965879c4900 100644 (file)
@@ -21,6 +21,7 @@
 #include <elf/dynamic-link.h>
 #include <tls.h>
 #include <dl-tlsdesc.h>
+#include <dl-unmap-segments.h>
 #include <tlsdeschtab.h>
 
 /* This function is used to lazily resolve TLS_DESC REL relocations
@@ -146,8 +147,7 @@ void
 internal_function
 _dl_unmap (struct link_map *map)
 {
-  __munmap ((void *) (map)->l_map_start,
-           (map)->l_map_end - (map)->l_map_start);
+  _dl_unmap_segments (map);
 
 #if SHARED
   /* _dl_unmap is only called for dlopen()ed libraries, for which
index 65cd709fef045b18c2e35db5fd267e38fabb83d4..6cf5e1b0b8a3500ca383bdd82e364344a48a35c9 100644 (file)
@@ -91,9 +91,7 @@ typedef struct link_map *lookup_t;
 
 /* Unmap a loaded object, called by _dl_close (). */
 #ifndef DL_UNMAP_IS_SPECIAL
-# define DL_UNMAP(map) \
- __munmap ((void *) (map)->l_map_start,                                              \
-          (map)->l_map_end - (map)->l_map_start)
+# define DL_UNMAP(map) _dl_unmap_segments (map)
 #endif
 
 /* By default we do not need special support to initialize DSOs loaded
index 9b6e359ae00c6c298f7c4c11c003eb6c777fe97b..ef4a1ddf98cf9bcae90e14aa68c4131b0b8d4d41 100644 (file)
@@ -21,6 +21,7 @@
 #include <elf/dynamic-link.h>
 #include <tls.h>
 #include <dl-tlsdesc.h>
+#include <dl-unmap-segments.h>
 #include <tlsdeschtab.h>
 
 /* The following 4 functions take an entry_check_offset argument.
@@ -258,8 +259,7 @@ void
 internal_function
 _dl_unmap (struct link_map *map)
 {
-  __munmap ((void *) (map)->l_map_start,
-           (map)->l_map_end - (map)->l_map_start);
+  _dl_unmap_segments (map);
 
 #if SHARED
   if (map->l_mach.tlsdesc_table)
index 3bfb83032011ed7c86b314ff02a5e8c3e2015547..8bc2911850f2795c6159ae0b5c79379ed13033f7 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <sys/mman.h>
 #include <arch/sim.h>
+#include <dl-unmap-segments.h>
 
 /* Like realpath(), but simplified: no dynamic memory use, no lstat(),
    no set_errno(), no valid "rpath" on error, etc.  This handles some
@@ -154,5 +155,5 @@ void internal_function
 _dl_unmap (struct link_map *l)
 {
   sim_dlclose (l->l_map_start);
-  __munmap ((void *) l->l_map_start, l->l_map_end - l->l_map_start);
+  _dl_unmap_segments (map);
 }
index 0daa87f8fd51f75a5e742dc038e871f5b9fc89c9..c1c5e033c0d2a400d4b8b940f350553de5921904 100644 (file)
@@ -21,6 +21,7 @@
 #include <elf/dynamic-link.h>
 #include <tls.h>
 #include <dl-tlsdesc.h>
+#include <dl-unmap-segments.h>
 #include <tlsdeschtab.h>
 
 /* The following 2 functions take a caller argument, that contains the
@@ -136,8 +137,7 @@ void
 internal_function
 _dl_unmap (struct link_map *map)
 {
-  __munmap ((void *) (map)->l_map_start,
-           (map)->l_map_end - (map)->l_map_start);
+  _dl_unmap_segments (map);
 
 #if SHARED
   /* _dl_unmap is only called for dlopen()ed libraries, for which