diff options
author | Roland McGrath <roland@hack.frob.com> | 2014-04-03 10:47:14 -0700 |
---|---|---|
committer | Roland McGrath <roland@hack.frob.com> | 2014-04-03 10:47:14 -0700 |
commit | fcccd51286acbf9c19ac57ab7143e257d58323fd (patch) | |
tree | 011b737ea7df67117682d2fd4e0a6998c8eca840 | |
parent | f6488e2b7f13529cde762d02a0352071c078ff9a (diff) | |
download | glibc-fcccd51286acbf9c19ac57ab7143e257d58323fd.tar.gz glibc-fcccd51286acbf9c19ac57ab7143e257d58323fd.tar.xz glibc-fcccd51286acbf9c19ac57ab7143e257d58323fd.zip |
Factor mmap/munmap of PT_LOAD segments out of _dl_map_object_from_fd et al.
-rw-r--r-- | ChangeLog | 23 | ||||
-rw-r--r-- | elf/dl-close.c | 2 | ||||
-rw-r--r-- | elf/dl-fptr.c | 4 | ||||
-rw-r--r-- | elf/dl-load.c | 221 | ||||
-rw-r--r-- | elf/dl-load.h | 135 | ||||
-rw-r--r-- | elf/dl-map-segments.h | 153 | ||||
-rw-r--r-- | elf/dl-unmap-segments.h | 35 | ||||
-rw-r--r-- | ports/ChangeLog.hppa | 5 | ||||
-rw-r--r-- | ports/sysdeps/hppa/dl-fptr.c | 4 | ||||
-rw-r--r-- | sysdeps/aarch64/tlsdesc.c | 4 | ||||
-rw-r--r-- | sysdeps/arm/tlsdesc.c | 4 | ||||
-rw-r--r-- | sysdeps/generic/ldsodefs.h | 4 | ||||
-rw-r--r-- | sysdeps/i386/tlsdesc.c | 4 | ||||
-rw-r--r-- | sysdeps/tile/dl-runtime.c | 3 | ||||
-rw-r--r-- | sysdeps/x86_64/tlsdesc.c | 4 |
15 files changed, 391 insertions, 214 deletions
diff --git a/ChangeLog b/ChangeLog index e36b689d29..7a1c10620a 100644 --- 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 diff --git a/elf/dl-close.c b/elf/dl-close.c index f36ea0f779..d74d55eb87 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -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); diff --git a/elf/dl-fptr.c b/elf/dl-fptr.c index 40cb40a3f1..f1478e26bb 100644 --- a/elf/dl-fptr.c +++ b/elf/dl-fptr.c @@ -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; diff --git a/elf/dl-load.c b/elf/dl-load.c index 16b101af0c..cfa7f2525f 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -38,39 +38,9 @@ #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> @@ -85,18 +55,6 @@ #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 index 0000000000..602840e5f8 --- /dev/null +++ b/elf/dl-load.h @@ -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 index 0000000000..71671cd573 --- /dev/null +++ b/elf/dl-map-segments.h @@ -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 index 0000000000..ce9c3fecfa --- /dev/null +++ b/elf/dl-unmap-segments.h @@ -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 */ diff --git a/ports/ChangeLog.hppa b/ports/ChangeLog.hppa index 61aeabc30b..4433ad5ca4 100644 --- a/ports/ChangeLog.hppa +++ b/ports/ChangeLog.hppa @@ -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. diff --git a/ports/sysdeps/hppa/dl-fptr.c b/ports/sysdeps/hppa/dl-fptr.c index 7404f5ff91..f975664dd6 100644 --- a/ports/sysdeps/hppa/dl-fptr.c +++ b/ports/sysdeps/hppa/dl-fptr.c @@ -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; diff --git a/sysdeps/aarch64/tlsdesc.c b/sysdeps/aarch64/tlsdesc.c index 0921230676..1b4181956c 100644 --- a/sysdeps/aarch64/tlsdesc.c +++ b/sysdeps/aarch64/tlsdesc.c @@ -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) diff --git a/sysdeps/arm/tlsdesc.c b/sysdeps/arm/tlsdesc.c index fc754d6c42..e52034ded6 100644 --- a/sysdeps/arm/tlsdesc.c +++ b/sysdeps/arm/tlsdesc.c @@ -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 diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 65cd709fef..6cf5e1b0b8 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -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 diff --git a/sysdeps/i386/tlsdesc.c b/sysdeps/i386/tlsdesc.c index 9b6e359ae0..ef4a1ddf98 100644 --- a/sysdeps/i386/tlsdesc.c +++ b/sysdeps/i386/tlsdesc.c @@ -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) diff --git a/sysdeps/tile/dl-runtime.c b/sysdeps/tile/dl-runtime.c index 3bfb830320..8bc2911850 100644 --- a/sysdeps/tile/dl-runtime.c +++ b/sysdeps/tile/dl-runtime.c @@ -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); } diff --git a/sysdeps/x86_64/tlsdesc.c b/sysdeps/x86_64/tlsdesc.c index 0daa87f8fd..c1c5e033c0 100644 --- a/sysdeps/x86_64/tlsdesc.c +++ b/sysdeps/x86_64/tlsdesc.c @@ -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 |