1 /* $NetBSD: map_object.c,v 1.29 2003/06/05 10:41:32 simonb Exp $ */ 2 3 /* 4 * Copyright 1996 John D. Polstra. 5 * Copyright 1996 Matt Thomas <matt@3am-software.com> 6 * Copyright 2002 Charles M. Hannum <root@ihack.net> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by John Polstra. 20 * 4. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <errno.h> 36 #include <stddef.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <sys/stat.h> 41 #include <sys/types.h> 42 #include <sys/mman.h> 43 44 #include "rtld.h" 45 46 static int protflags __P((int)); /* Elf flags -> mmap protection */ 47 48 /* 49 * Map a shared object into memory. The argument is a file descriptor, 50 * which must be open on the object and positioned at its beginning. 51 * 52 * The return value is a pointer to a newly-allocated Obj_Entry structure 53 * for the shared object. Returns NULL on failure. 54 */ 55 Obj_Entry * 56 _rtld_map_object(path, fd, sb) 57 char *path; 58 int fd; 59 const struct stat *sb; 60 { 61 Obj_Entry *obj; 62 Elf_Ehdr *ehdr; 63 Elf_Phdr *phdr; 64 Elf_Phdr *phlimit; 65 Elf_Phdr *segs[2]; 66 int nsegs; 67 caddr_t mapbase = MAP_FAILED; 68 size_t mapsize; 69 int mapflags; 70 Elf_Off base_offset; 71 #ifdef MAP_ALIGNED 72 Elf_Addr base_alignment; 73 #endif 74 Elf_Addr base_vaddr; 75 Elf_Addr base_vlimit; 76 Elf_Addr text_vlimit; 77 int text_flags; 78 caddr_t base_addr; 79 Elf_Off data_offset; 80 Elf_Addr data_vaddr; 81 Elf_Addr data_vlimit; 82 int data_flags; 83 caddr_t data_addr; 84 caddr_t gap_addr; 85 size_t gap_size; 86 #ifdef RTLD_LOADER 87 Elf_Addr clear_vaddr; 88 caddr_t clear_addr; 89 size_t nclear; 90 #endif 91 92 if (sb != NULL && sb->st_size < sizeof (Elf_Ehdr)) { 93 _rtld_error("%s: unrecognized file format", path); 94 return NULL; 95 } 96 97 obj = _rtld_obj_new(); 98 obj->path = path; 99 obj->pathlen = strlen(path); 100 if (sb != NULL) { 101 obj->dev = sb->st_dev; 102 obj->ino = sb->st_ino; 103 } 104 105 ehdr = mmap(NULL, _rtld_pagesz, PROT_READ, MAP_FILE | MAP_SHARED, fd, 106 (off_t)0); 107 if (ehdr == MAP_FAILED) { 108 _rtld_error("%s: read error: %s", path, xstrerror(errno)); 109 goto bad; 110 } 111 /* Make sure the file is valid */ 112 if (memcmp(ELFMAG, ehdr->e_ident, SELFMAG) != 0 || 113 ehdr->e_ident[EI_CLASS] != ELFCLASS) { 114 _rtld_error("%s: unrecognized file format", path); 115 goto bad; 116 } 117 /* Elf_e_ident includes class */ 118 if (ehdr->e_ident[EI_VERSION] != EV_CURRENT || 119 ehdr->e_version != EV_CURRENT || 120 ehdr->e_ident[EI_DATA] != ELFDEFNNAME(MACHDEP_ENDIANNESS)) { 121 _rtld_error("%s: unsupported file version", path); 122 goto bad; 123 } 124 if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { 125 _rtld_error("%s: unsupported file type", path); 126 goto bad; 127 } 128 switch (ehdr->e_machine) { 129 ELFDEFNNAME(MACHDEP_ID_CASES) 130 default: 131 _rtld_error("%s: unsupported machine", path); 132 goto bad; 133 } 134 135 /* 136 * We rely on the program header being in the first page. This is 137 * not strictly required by the ABI specification, but it seems to 138 * always true in practice. And, it simplifies things considerably. 139 */ 140 assert(ehdr->e_phentsize == sizeof(Elf_Phdr)); 141 assert(ehdr->e_phoff + ehdr->e_phnum * sizeof(Elf_Phdr) <= 142 _rtld_pagesz); 143 144 /* 145 * Scan the program header entries, and save key information. 146 * 147 * We rely on there being exactly two load segments, text and data, 148 * in that order. 149 */ 150 phdr = (Elf_Phdr *) ((caddr_t)ehdr + ehdr->e_phoff); 151 phlimit = phdr + ehdr->e_phnum; 152 nsegs = 0; 153 while (phdr < phlimit) { 154 switch (phdr->p_type) { 155 case PT_INTERP: 156 obj->interp = (void *)phdr->p_vaddr; 157 break; 158 159 case PT_LOAD: 160 if (nsegs < 2) 161 segs[nsegs] = phdr; 162 ++nsegs; 163 break; 164 165 case PT_DYNAMIC: 166 obj->dynamic = (void *)phdr->p_vaddr; 167 break; 168 } 169 170 ++phdr; 171 } 172 obj->entry = (void *)ehdr->e_entry; 173 if (!obj->dynamic) { 174 _rtld_error("%s: not dynamically linked", path); 175 goto bad; 176 } 177 if (nsegs != 2) { 178 _rtld_error("%s: wrong number of segments (%d != 2)", path, 179 nsegs); 180 goto bad; 181 } 182 183 /* 184 * Map the entire address space of the object as a file 185 * region to stake out our contiguous region and establish a 186 * base for relocation. We use a file mapping so that 187 * the kernel will give us whatever alignment is appropriate 188 * for the platform we're running on. 189 * 190 * We map it using the text protection, map the data segment 191 * into the right place, then map an anon segment for the bss 192 * and unmap the gaps left by padding to alignment. 193 */ 194 195 #ifdef MAP_ALIGNED 196 base_alignment = segs[0]->p_align; 197 #endif 198 base_offset = round_down(segs[0]->p_offset); 199 base_vaddr = round_down(segs[0]->p_vaddr); 200 base_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_memsz); 201 text_vlimit = round_up(segs[0]->p_vaddr + segs[0]->p_memsz); 202 text_flags = protflags(segs[0]->p_flags); 203 data_offset = round_down(segs[1]->p_offset); 204 data_vaddr = round_down(segs[1]->p_vaddr); 205 data_vlimit = round_up(segs[1]->p_vaddr + segs[1]->p_filesz); 206 data_flags = protflags(segs[1]->p_flags); 207 #ifdef RTLD_LOADER 208 clear_vaddr = segs[1]->p_vaddr + segs[1]->p_filesz; 209 #endif 210 211 obj->textsize = text_vlimit - base_vaddr; 212 obj->vaddrbase = base_vaddr; 213 obj->isdynamic = ehdr->e_type == ET_DYN; 214 215 munmap(ehdr, _rtld_pagesz); 216 ehdr = MAP_FAILED; 217 218 /* 219 * Calculate log2 of the base section alignment. 220 */ 221 mapflags = 0; 222 #ifdef MAP_ALIGNED 223 if (base_alignment > _rtld_pagesz) { 224 unsigned int log2 = 0; 225 for (; base_alignment > 1; base_alignment >>= 1) 226 log2++; 227 mapflags = MAP_ALIGNED(log2); 228 } 229 #endif 230 231 #ifdef RTLD_LOADER 232 base_addr = obj->isdynamic ? NULL : (caddr_t)base_vaddr; 233 #else 234 base_addr = NULL; 235 #endif 236 mapsize = base_vlimit - base_vaddr; 237 mapbase = mmap(base_addr, mapsize, text_flags, 238 mapflags | MAP_FILE | MAP_PRIVATE, fd, base_offset); 239 if (mapbase == MAP_FAILED) { 240 _rtld_error("mmap of entire address space failed: %s", 241 xstrerror(errno)); 242 goto bad; 243 } 244 245 /* Overlay the data segment onto the proper region. */ 246 data_addr = mapbase + (data_vaddr - base_vaddr); 247 if (mmap(data_addr, data_vlimit - data_vaddr, data_flags, 248 MAP_FILE | MAP_PRIVATE | MAP_FIXED, fd, data_offset) == 249 MAP_FAILED) { 250 _rtld_error("mmap of data failed: %s", xstrerror(errno)); 251 goto bad; 252 } 253 254 /* Overlay the bss segment onto the proper region. */ 255 if (mmap(mapbase + data_vlimit - base_vaddr, base_vlimit - data_vlimit, 256 data_flags, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) == 257 MAP_FAILED) { 258 _rtld_error("mmap of bss failed: %s", xstrerror(errno)); 259 goto bad; 260 } 261 262 /* Unmap the gap between the text and data. */ 263 gap_addr = mapbase + round_up(text_vlimit - base_vaddr); 264 gap_size = data_addr - gap_addr; 265 if (gap_size != 0 && mprotect(gap_addr, gap_size, PROT_NONE) == -1) { 266 _rtld_error("mprotect of text -> data gap failed: %s", 267 xstrerror(errno)); 268 goto bad; 269 } 270 271 #ifdef RTLD_LOADER 272 /* Clear any BSS in the last page of the data segment. */ 273 clear_addr = mapbase + (clear_vaddr - base_vaddr); 274 if ((nclear = data_vlimit - clear_vaddr) > 0) 275 memset(clear_addr, 0, nclear); 276 277 /* Non-file portion of BSS mapped above. */ 278 #endif 279 280 obj->mapbase = mapbase; 281 obj->mapsize = mapsize; 282 obj->relocbase = mapbase - base_vaddr; 283 284 if (obj->dynamic) 285 obj->dynamic = (void *)(obj->relocbase + (Elf_Addr)obj->dynamic); 286 if (obj->entry) 287 obj->entry = (void *)(obj->relocbase + (Elf_Addr)obj->entry); 288 if (obj->interp) 289 obj->interp = (void *)(obj->relocbase + (Elf_Addr)obj->interp); 290 291 return obj; 292 293 bad: 294 if (ehdr != MAP_FAILED) 295 munmap(ehdr, _rtld_pagesz); 296 if (mapbase != MAP_FAILED) 297 munmap(mapbase, mapsize); 298 _rtld_obj_free(obj); 299 return NULL; 300 } 301 302 void 303 _rtld_obj_free(obj) 304 Obj_Entry *obj; 305 { 306 Objlist_Entry *elm; 307 308 free(obj->path); 309 while (obj->needed != NULL) { 310 Needed_Entry *needed = obj->needed; 311 obj->needed = needed->next; 312 free(needed); 313 } 314 while ((elm = SIMPLEQ_FIRST(&obj->dldags)) != NULL) { 315 SIMPLEQ_REMOVE_HEAD(&obj->dldags, link); 316 free(elm); 317 } 318 while ((elm = SIMPLEQ_FIRST(&obj->dagmembers)) != NULL) { 319 SIMPLEQ_REMOVE_HEAD(&obj->dagmembers, link); 320 free(elm); 321 } 322 free(obj); 323 } 324 325 Obj_Entry * 326 _rtld_obj_new(void) 327 { 328 Obj_Entry *obj; 329 330 obj = CNEW(Obj_Entry); 331 SIMPLEQ_INIT(&obj->dldags); 332 SIMPLEQ_INIT(&obj->dagmembers); 333 return obj; 334 } 335 336 /* 337 * Given a set of ELF protection flags, return the corresponding protection 338 * flags for MMAP. 339 */ 340 static int 341 protflags(elfflags) 342 int elfflags; 343 { 344 int prot = 0; 345 346 if (elfflags & PF_R) 347 prot |= PROT_READ; 348 #ifdef RTLD_LOADER 349 if (elfflags & PF_W) 350 prot |= PROT_WRITE; 351 #endif 352 if (elfflags & PF_X) 353 prot |= PROT_EXEC; 354 return prot; 355 } 356