| 1 | /* Map in a shared object's segments.  Generic version. | 
|---|
| 2 | Copyright (C) 1995-2020 Free Software Foundation, Inc. | 
|---|
| 3 | This file is part of the GNU C Library. | 
|---|
| 4 |  | 
|---|
| 5 | The GNU C Library is free software; you can redistribute it and/or | 
|---|
| 6 | modify it under the terms of the GNU Lesser General Public | 
|---|
| 7 | License as published by the Free Software Foundation; either | 
|---|
| 8 | version 2.1 of the License, or (at your option) any later version. | 
|---|
| 9 |  | 
|---|
| 10 | The GNU C Library is distributed in the hope that it will be useful, | 
|---|
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|---|
| 13 | Lesser General Public License for more details. | 
|---|
| 14 |  | 
|---|
| 15 | You should have received a copy of the GNU Lesser General Public | 
|---|
| 16 | License along with the GNU C Library; if not, see | 
|---|
| 17 | <https://www.gnu.org/licenses/>.  */ | 
|---|
| 18 |  | 
|---|
| 19 | #include <dl-load.h> | 
|---|
| 20 |  | 
|---|
| 21 | /* This implementation assumes (as does the corresponding implementation | 
|---|
| 22 | of _dl_unmap_segments, in dl-unmap-segments.h) that shared objects | 
|---|
| 23 | are always laid out with all segments contiguous (or with gaps | 
|---|
| 24 | between them small enough that it's preferable to reserve all whole | 
|---|
| 25 | pages inside the gaps with PROT_NONE mappings rather than permitting | 
|---|
| 26 | other use of those parts of the address space).  */ | 
|---|
| 27 |  | 
|---|
| 28 | static __always_inline const char * | 
|---|
| 29 | _dl_map_segments (struct link_map *l, int fd, | 
|---|
| 30 | const ElfW(Ehdr) *, int type, | 
|---|
| 31 | const struct loadcmd loadcmds[], size_t nloadcmds, | 
|---|
| 32 | const size_t maplength, bool has_holes, | 
|---|
| 33 | struct link_map *loader) | 
|---|
| 34 | { | 
|---|
| 35 | const struct loadcmd *c = loadcmds; | 
|---|
| 36 |  | 
|---|
| 37 | if (__glibc_likely (type == ET_DYN)) | 
|---|
| 38 | { | 
|---|
| 39 | /* This is a position-independent shared object.  We can let the | 
|---|
| 40 | kernel map it anywhere it likes, but we must have space for all | 
|---|
| 41 | the segments in their specified positions relative to the first. | 
|---|
| 42 | So we map the first segment without MAP_FIXED, but with its | 
|---|
| 43 | extent increased to cover all the segments.  Then we remove | 
|---|
| 44 | access from excess portion, and there is known sufficient space | 
|---|
| 45 | there to remap from the later segments. | 
|---|
| 46 |  | 
|---|
| 47 | As a refinement, sometimes we have an address that we would | 
|---|
| 48 | prefer to map such objects at; but this is only a preference, | 
|---|
| 49 | the OS can do whatever it likes. */ | 
|---|
| 50 | ElfW(Addr) mappref | 
|---|
| 51 | = (ELF_PREFERRED_ADDRESS (loader, maplength, | 
|---|
| 52 | c->mapstart & GLRO(dl_use_load_bias)) | 
|---|
| 53 | - MAP_BASE_ADDR (l)); | 
|---|
| 54 |  | 
|---|
| 55 | /* Remember which part of the address space this object uses.  */ | 
|---|
| 56 | l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength, | 
|---|
| 57 | c->prot, | 
|---|
| 58 | MAP_COPY|MAP_FILE, | 
|---|
| 59 | fd, c->mapoff); | 
|---|
| 60 | if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED)) | 
|---|
| 61 | return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; | 
|---|
| 62 |  | 
|---|
| 63 | l->l_map_end = l->l_map_start + maplength; | 
|---|
| 64 | l->l_addr = l->l_map_start - c->mapstart; | 
|---|
| 65 |  | 
|---|
| 66 | if (has_holes) | 
|---|
| 67 | { | 
|---|
| 68 | /* Change protection on the excess portion to disallow all access; | 
|---|
| 69 | the portions we do not remap later will be inaccessible as if | 
|---|
| 70 | unallocated.  Then jump into the normal segment-mapping loop to | 
|---|
| 71 | handle the portion of the segment past the end of the file | 
|---|
| 72 | mapping.  */ | 
|---|
| 73 | if (__glibc_unlikely | 
|---|
| 74 | (__mprotect ((caddr_t) (l->l_addr + c->mapend), | 
|---|
| 75 | loadcmds[nloadcmds - 1].mapstart - c->mapend, | 
|---|
| 76 | PROT_NONE) < 0)) | 
|---|
| 77 | return DL_MAP_SEGMENTS_ERROR_MPROTECT; | 
|---|
| 78 | } | 
|---|
| 79 |  | 
|---|
| 80 | l->l_contiguous = 1; | 
|---|
| 81 |  | 
|---|
| 82 | goto postmap; | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | /* Remember which part of the address space this object uses.  */ | 
|---|
| 86 | l->l_map_start = c->mapstart + l->l_addr; | 
|---|
| 87 | l->l_map_end = l->l_map_start + maplength; | 
|---|
| 88 | l->l_contiguous = !has_holes; | 
|---|
| 89 |  | 
|---|
| 90 | while (c < &loadcmds[nloadcmds]) | 
|---|
| 91 | { | 
|---|
| 92 | if (c->mapend > c->mapstart | 
|---|
| 93 | /* Map the segment contents from the file.  */ | 
|---|
| 94 | && (__mmap ((void *) (l->l_addr + c->mapstart), | 
|---|
| 95 | c->mapend - c->mapstart, c->prot, | 
|---|
| 96 | MAP_FIXED|MAP_COPY|MAP_FILE, | 
|---|
| 97 | fd, c->mapoff) | 
|---|
| 98 | == MAP_FAILED)) | 
|---|
| 99 | return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; | 
|---|
| 100 |  | 
|---|
| 101 | postmap: | 
|---|
| 102 | _dl_postprocess_loadcmd (l, header, c); | 
|---|
| 103 |  | 
|---|
| 104 | if (c->allocend > c->dataend) | 
|---|
| 105 | { | 
|---|
| 106 | /* Extra zero pages should appear at the end of this segment, | 
|---|
| 107 | after the data mapped from the file.   */ | 
|---|
| 108 | ElfW(Addr) zero, zeroend, zeropage; | 
|---|
| 109 |  | 
|---|
| 110 | zero = l->l_addr + c->dataend; | 
|---|
| 111 | zeroend = l->l_addr + c->allocend; | 
|---|
| 112 | zeropage = ((zero + GLRO(dl_pagesize) - 1) | 
|---|
| 113 | & ~(GLRO(dl_pagesize) - 1)); | 
|---|
| 114 |  | 
|---|
| 115 | if (zeroend < zeropage) | 
|---|
| 116 | /* All the extra data is in the last page of the segment. | 
|---|
| 117 | We can just zero it.  */ | 
|---|
| 118 | zeropage = zeroend; | 
|---|
| 119 |  | 
|---|
| 120 | if (zeropage > zero) | 
|---|
| 121 | { | 
|---|
| 122 | /* Zero the final part of the last page of the segment.  */ | 
|---|
| 123 | if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0)) | 
|---|
| 124 | { | 
|---|
| 125 | /* Dag nab it.  */ | 
|---|
| 126 | if (__mprotect ((caddr_t) (zero | 
|---|
| 127 | & ~(GLRO(dl_pagesize) - 1)), | 
|---|
| 128 | GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0) | 
|---|
| 129 | return DL_MAP_SEGMENTS_ERROR_MPROTECT; | 
|---|
| 130 | } | 
|---|
| 131 | memset ((void *) zero, '\0', zeropage - zero); | 
|---|
| 132 | if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0)) | 
|---|
| 133 | __mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)), | 
|---|
| 134 | GLRO(dl_pagesize), c->prot); | 
|---|
| 135 | } | 
|---|
| 136 |  | 
|---|
| 137 | if (zeroend > zeropage) | 
|---|
| 138 | { | 
|---|
| 139 | /* Map the remaining zero pages in from the zero fill FD.  */ | 
|---|
| 140 | caddr_t mapat; | 
|---|
| 141 | mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage, | 
|---|
| 142 | c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED, | 
|---|
| 143 | -1, 0); | 
|---|
| 144 | if (__glibc_unlikely (mapat == MAP_FAILED)) | 
|---|
| 145 | return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL; | 
|---|
| 146 | } | 
|---|
| 147 | } | 
|---|
| 148 |  | 
|---|
| 149 | ++c; | 
|---|
| 150 | } | 
|---|
| 151 |  | 
|---|
| 152 | /* Notify ELF_PREFERRED_ADDRESS that we have to load this one | 
|---|
| 153 | fixed.  */ | 
|---|
| 154 | ELF_FIXED_ADDRESS (loader, c->mapstart); | 
|---|
| 155 |  | 
|---|
| 156 | return NULL; | 
|---|
| 157 | } | 
|---|
| 158 |  | 
|---|