1 /*- 2 * Copyright 1996-1998 John D. Polstra. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 * $FreeBSD: src/libexec/rtld-elf/map_object.c,v 1.7.2.2 2002/12/28 19:49:41 dillon Exp $ 26 */ 27 28 #include <sys/param.h> 29 #include <sys/mman.h> 30 #include <sys/stat.h> 31 32 #include <errno.h> 33 #include <stddef.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #include "rtld.h" 39 40 static int convert_prot(int); /* Elf flags -> mmap protection */ 41 static int convert_flags(int); /* Elf flags -> mmap flags */ 42 43 /* 44 * Map a shared object into memory. The "fd" argument is a file descriptor, 45 * which must be open on the object and positioned at its beginning. 46 * The "path" argument is a pathname that is used only for error messages. 47 * 48 * The return value is a pointer to a newly-allocated Obj_Entry structure 49 * for the shared object. Returns NULL on failure. 50 */ 51 Obj_Entry * 52 map_object(int fd, const char *path, const struct stat *sb) 53 { 54 Obj_Entry *obj; 55 union { 56 Elf_Ehdr hdr; 57 char buf[PAGE_SIZE]; 58 } u; 59 int nbytes, i; 60 Elf_Phdr *phdr; 61 Elf_Phdr *phlimit; 62 Elf_Phdr **segs; 63 int nsegs; 64 Elf_Phdr *phdyn; 65 Elf_Phdr *phphdr; 66 Elf_Phdr *phinterp; 67 caddr_t mapbase; 68 size_t mapsize; 69 Elf_Off base_offset; 70 Elf_Addr base_vaddr; 71 Elf_Addr base_vlimit; 72 caddr_t base_addr; 73 Elf_Off data_offset; 74 Elf_Addr data_vaddr; 75 Elf_Addr data_vlimit; 76 caddr_t data_addr; 77 int data_prot; 78 int data_flags; 79 Elf_Addr clear_vaddr; 80 caddr_t clear_addr; 81 caddr_t clear_page; 82 size_t nclear; 83 Elf_Addr bss_vaddr; 84 Elf_Addr bss_vlimit; 85 caddr_t bss_addr; 86 87 if ((nbytes = read(fd, u.buf, PAGE_SIZE)) == -1) { 88 _rtld_error("%s: read error: %s", path, strerror(errno)); 89 return NULL; 90 } 91 92 /* Make sure the file is valid */ 93 if (nbytes < sizeof(Elf_Ehdr) 94 || u.hdr.e_ident[EI_MAG0] != ELFMAG0 95 || u.hdr.e_ident[EI_MAG1] != ELFMAG1 96 || u.hdr.e_ident[EI_MAG2] != ELFMAG2 97 || u.hdr.e_ident[EI_MAG3] != ELFMAG3) { 98 _rtld_error("%s: invalid file format", path); 99 return NULL; 100 } 101 if (u.hdr.e_ident[EI_CLASS] != ELF_TARG_CLASS 102 || u.hdr.e_ident[EI_DATA] != ELF_TARG_DATA) { 103 _rtld_error("%s: unsupported file layout", path); 104 return NULL; 105 } 106 if (u.hdr.e_ident[EI_VERSION] != EV_CURRENT 107 || u.hdr.e_version != EV_CURRENT) { 108 _rtld_error("%s: unsupported file version", path); 109 return NULL; 110 } 111 if (u.hdr.e_type != ET_EXEC && u.hdr.e_type != ET_DYN) { 112 _rtld_error("%s: unsupported file type", path); 113 return NULL; 114 } 115 if (u.hdr.e_machine != ELF_TARG_MACH) { 116 _rtld_error("%s: unsupported machine", path); 117 return NULL; 118 } 119 120 /* 121 * We rely on the program header being in the first page. This is 122 * not strictly required by the ABI specification, but it seems to 123 * always true in practice. And, it simplifies things considerably. 124 */ 125 if (u.hdr.e_phentsize != sizeof(Elf_Phdr)) { 126 _rtld_error( 127 "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path); 128 return NULL; 129 } 130 if (u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf_Phdr) > nbytes) { 131 _rtld_error("%s: program header too large", path); 132 return NULL; 133 } 134 135 /* 136 * Scan the program header entries, and save key information. 137 * 138 * We rely on there being exactly two load segments, text and data, 139 * in that order. 140 */ 141 phdr = (Elf_Phdr *) (u.buf + u.hdr.e_phoff); 142 phlimit = phdr + u.hdr.e_phnum; 143 nsegs = -1; 144 phdyn = phphdr = phinterp = NULL; 145 segs = alloca(sizeof(segs[0]) * u.hdr.e_phnum); 146 while (phdr < phlimit) { 147 switch (phdr->p_type) { 148 149 case PT_INTERP: 150 phinterp = phdr; 151 break; 152 153 case PT_LOAD: 154 segs[++nsegs] = phdr; 155 if (segs[nsegs]->p_align < PAGE_SIZE) { 156 _rtld_error("%s: PT_LOAD segment %d not page-aligned", 157 path, nsegs); 158 return NULL; 159 } 160 break; 161 162 case PT_PHDR: 163 phphdr = phdr; 164 break; 165 166 case PT_DYNAMIC: 167 phdyn = phdr; 168 break; 169 } 170 171 ++phdr; 172 } 173 if (phdyn == NULL) { 174 _rtld_error("%s: object is not dynamically-linked", path); 175 return NULL; 176 } 177 178 if (nsegs < 0) { 179 _rtld_error("%s: too few PT_LOAD segments", path); 180 return NULL; 181 } 182 183 /* 184 * Map the entire address space of the object, to stake out our 185 * contiguous region, and to establish the base address for relocation. 186 */ 187 base_offset = trunc_page(segs[0]->p_offset); 188 base_vaddr = trunc_page(segs[0]->p_vaddr); 189 base_vlimit = round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz); 190 mapsize = base_vlimit - base_vaddr; 191 base_addr = u.hdr.e_type == ET_EXEC ? (caddr_t) base_vaddr : NULL; 192 193 mapbase = mmap(base_addr, mapsize, convert_prot(segs[0]->p_flags), 194 convert_flags(segs[0]->p_flags), fd, base_offset); 195 if (mapbase == (caddr_t) -1) { 196 _rtld_error("%s: mmap of entire address space failed: %s", 197 path, strerror(errno)); 198 return NULL; 199 } 200 if (base_addr != NULL && mapbase != base_addr) { 201 _rtld_error("%s: mmap returned wrong address: wanted %p, got %p", 202 path, base_addr, mapbase); 203 munmap(mapbase, mapsize); 204 return NULL; 205 } 206 207 for (i = 0; i <= nsegs; i++) { 208 /* Overlay the segment onto the proper region. */ 209 data_offset = trunc_page(segs[i]->p_offset); 210 data_vaddr = trunc_page(segs[i]->p_vaddr); 211 data_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_filesz); 212 data_addr = mapbase + (data_vaddr - base_vaddr); 213 data_prot = convert_prot(segs[i]->p_flags); 214 data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED; 215 /* Do not call mmap on the first segment - this is redundant */ 216 if (i && mmap(data_addr, data_vlimit - data_vaddr, data_prot, 217 data_flags, fd, data_offset) == (caddr_t) -1) { 218 _rtld_error("%s: mmap of data failed: %s", path, strerror(errno)); 219 return NULL; 220 } 221 222 /* Clear any BSS in the last page of the segment. */ 223 clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz; 224 clear_addr = mapbase + (clear_vaddr - base_vaddr); 225 clear_page = mapbase + (trunc_page(clear_vaddr) - base_vaddr); 226 if ((nclear = data_vlimit - clear_vaddr) > 0) { 227 /* Make sure the end of the segment is writable */ 228 if ((data_prot & PROT_WRITE) == 0 && 229 -1 == mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) { 230 _rtld_error("%s: mprotect failed: %s", path, 231 strerror(errno)); 232 return NULL; 233 } 234 235 memset(clear_addr, 0, nclear); 236 237 /* Reset the data protection back */ 238 if ((data_prot & PROT_WRITE) == 0) 239 mprotect(clear_page, PAGE_SIZE, data_prot); 240 } 241 242 /* Overlay the BSS segment onto the proper region. */ 243 bss_vaddr = data_vlimit; 244 bss_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_memsz); 245 bss_addr = mapbase + (bss_vaddr - base_vaddr); 246 if (bss_vlimit > bss_vaddr) { /* There is something to do */ 247 if (mmap(bss_addr, bss_vlimit - bss_vaddr, data_prot, 248 MAP_PRIVATE|MAP_FIXED|MAP_ANON, -1, 0) == (caddr_t) -1) { 249 _rtld_error("%s: mmap of bss failed: %s", path, 250 strerror(errno)); 251 return NULL; 252 } 253 } 254 } 255 256 obj = obj_new(); 257 if (sb != NULL) { 258 obj->dev = sb->st_dev; 259 obj->ino = sb->st_ino; 260 } 261 obj->mapbase = mapbase; 262 obj->mapsize = mapsize; 263 obj->textsize = round_page(segs[0]->p_vaddr + segs[0]->p_memsz) - 264 base_vaddr; 265 obj->vaddrbase = base_vaddr; 266 obj->relocbase = mapbase - base_vaddr; 267 obj->dynamic = (const Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr); 268 if (u.hdr.e_entry != 0) 269 obj->entry = (caddr_t) (obj->relocbase + u.hdr.e_entry); 270 if (phphdr != NULL) { 271 obj->phdr = (const Elf_Phdr *) (obj->relocbase + phphdr->p_vaddr); 272 obj->phsize = phphdr->p_memsz; 273 } 274 if (phinterp != NULL) 275 obj->interp = (const char *) (obj->relocbase + phinterp->p_vaddr); 276 277 return obj; 278 } 279 280 void 281 obj_free(Obj_Entry *obj) 282 { 283 Objlist_Entry *elm; 284 285 free(obj->path); 286 while (obj->needed != NULL) { 287 Needed_Entry *needed = obj->needed; 288 obj->needed = needed->next; 289 free(needed); 290 } 291 while (!STAILQ_EMPTY(&obj->dldags)) { 292 elm = STAILQ_FIRST(&obj->dldags); 293 STAILQ_REMOVE_HEAD(&obj->dldags, link); 294 free(elm); 295 } 296 while (!STAILQ_EMPTY(&obj->dagmembers)) { 297 elm = STAILQ_FIRST(&obj->dagmembers); 298 STAILQ_REMOVE_HEAD(&obj->dagmembers, link); 299 free(elm); 300 } 301 free(obj); 302 } 303 304 Obj_Entry * 305 obj_new(void) 306 { 307 Obj_Entry *obj; 308 309 obj = CNEW(Obj_Entry); 310 STAILQ_INIT(&obj->dldags); 311 STAILQ_INIT(&obj->dagmembers); 312 return obj; 313 } 314 315 /* 316 * Given a set of ELF protection flags, return the corresponding protection 317 * flags for MMAP. 318 */ 319 static int 320 convert_prot(int elfflags) 321 { 322 int prot = 0; 323 if (elfflags & PF_R) 324 prot |= PROT_READ; 325 if (elfflags & PF_W) 326 prot |= PROT_WRITE; 327 if (elfflags & PF_X) 328 prot |= PROT_EXEC; 329 return prot; 330 } 331 332 static int 333 convert_flags(int elfflags) 334 { 335 int flags = MAP_PRIVATE; /* All mappings are private */ 336 337 /* 338 * Readonly mappings are marked "MAP_NOCORE", because they can be 339 * reconstructed by a debugger. 340 */ 341 if (!(elfflags & PF_W)) 342 flags |= MAP_NOCORE; 343 return flags; 344 } 345