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$ 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 "debug.h" 39 #include "rtld.h" 40 41 static int protflags(int); /* Elf flags -> mmap protection */ 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 Elf_Addr clear_vaddr; 79 caddr_t clear_addr; 80 caddr_t clear_page; 81 size_t nclear; 82 Elf_Addr bss_vaddr; 83 Elf_Addr bss_vlimit; 84 caddr_t bss_addr; 85 86 if ((nbytes = read(fd, u.buf, PAGE_SIZE)) == -1) { 87 _rtld_error("%s: read error: %s", path, strerror(errno)); 88 return NULL; 89 } 90 91 /* Make sure the file is valid */ 92 if (nbytes < sizeof(Elf_Ehdr) 93 || u.hdr.e_ident[EI_MAG0] != ELFMAG0 94 || u.hdr.e_ident[EI_MAG1] != ELFMAG1 95 || u.hdr.e_ident[EI_MAG2] != ELFMAG2 96 || u.hdr.e_ident[EI_MAG3] != ELFMAG3) { 97 _rtld_error("%s: invalid file format", path); 98 return NULL; 99 } 100 if (u.hdr.e_ident[EI_CLASS] != ELF_TARG_CLASS 101 || u.hdr.e_ident[EI_DATA] != ELF_TARG_DATA) { 102 _rtld_error("%s: unsupported file layout", path); 103 return NULL; 104 } 105 if (u.hdr.e_ident[EI_VERSION] != EV_CURRENT 106 || u.hdr.e_version != EV_CURRENT) { 107 _rtld_error("%s: unsupported file version", path); 108 return NULL; 109 } 110 if (u.hdr.e_type != ET_EXEC && u.hdr.e_type != ET_DYN) { 111 _rtld_error("%s: unsupported file type", path); 112 return NULL; 113 } 114 if (u.hdr.e_machine != ELF_TARG_MACH) { 115 _rtld_error("%s: unsupported machine", path); 116 return NULL; 117 } 118 119 /* 120 * We rely on the program header being in the first page. This is 121 * not strictly required by the ABI specification, but it seems to 122 * always true in practice. And, it simplifies things considerably. 123 */ 124 if (u.hdr.e_phentsize != sizeof(Elf_Phdr)) { 125 _rtld_error( 126 "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path); 127 return NULL; 128 } 129 if (u.hdr.e_phoff + u.hdr.e_phnum*sizeof(Elf_Phdr) > nbytes) { 130 _rtld_error("%s: program header too large", path); 131 return NULL; 132 } 133 134 /* 135 * Scan the program header entries, and save key information. 136 * 137 * We rely on there being exactly two load segments, text and data, 138 * in that order. 139 */ 140 phdr = (Elf_Phdr *) (u.buf + u.hdr.e_phoff); 141 phlimit = phdr + u.hdr.e_phnum; 142 nsegs = -1; 143 phdyn = phphdr = phinterp = NULL; 144 segs = alloca(sizeof(segs[0]) * u.hdr.e_phnum); 145 while (phdr < phlimit) { 146 switch (phdr->p_type) { 147 148 case PT_INTERP: 149 phinterp = phdr; 150 break; 151 152 case PT_LOAD: 153 segs[++nsegs] = phdr; 154 if (segs[nsegs]->p_align < PAGE_SIZE) { 155 _rtld_error("%s: PT_LOAD segment %d not page-aligned", 156 path, nsegs); 157 return NULL; 158 } 159 break; 160 161 case PT_PHDR: 162 phphdr = phdr; 163 break; 164 165 case PT_DYNAMIC: 166 phdyn = phdr; 167 break; 168 } 169 170 ++phdr; 171 } 172 if (phdyn == NULL) { 173 _rtld_error("%s: object is not dynamically-linked", path); 174 return NULL; 175 } 176 177 if (nsegs < 0) { 178 _rtld_error("%s: too few PT_LOAD segments", path); 179 return NULL; 180 } 181 182 /* 183 * Map the entire address space of the object, to stake out our 184 * contiguous region, and to establish the base address for relocation. 185 */ 186 base_offset = trunc_page(segs[0]->p_offset); 187 base_vaddr = trunc_page(segs[0]->p_vaddr); 188 base_vlimit = round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz); 189 mapsize = base_vlimit - base_vaddr; 190 base_addr = u.hdr.e_type == ET_EXEC ? (caddr_t) base_vaddr : NULL; 191 192 mapbase = mmap(base_addr, mapsize, protflags(segs[0]->p_flags), 193 MAP_PRIVATE, fd, base_offset); 194 if (mapbase == (caddr_t) -1) { 195 _rtld_error("%s: mmap of entire address space failed: %s", 196 path, strerror(errno)); 197 return NULL; 198 } 199 if (base_addr != NULL && mapbase != base_addr) { 200 _rtld_error("%s: mmap returned wrong address: wanted %p, got %p", 201 path, base_addr, mapbase); 202 munmap(mapbase, mapsize); 203 return NULL; 204 } 205 206 for (i = 0; i <= nsegs; i++) { 207 /* Overlay the segment onto the proper region. */ 208 data_offset = trunc_page(segs[i]->p_offset); 209 data_vaddr = trunc_page(segs[i]->p_vaddr); 210 data_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_filesz); 211 data_addr = mapbase + (data_vaddr - base_vaddr); 212 data_prot = protflags(segs[i]->p_flags); 213 /* Do not call mmap on the first segment - this is redundant */ 214 if (i && mmap(data_addr, data_vlimit - data_vaddr, data_prot, 215 MAP_PRIVATE|MAP_FIXED, fd, data_offset) == (caddr_t) -1) { 216 _rtld_error("%s: mmap of data failed: %s", path, strerror(errno)); 217 return NULL; 218 } 219 220 /* Clear any BSS in the last page of the segment. */ 221 clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz; 222 clear_addr = mapbase + (clear_vaddr - base_vaddr); 223 clear_page = mapbase + (trunc_page(clear_vaddr) - base_vaddr); 224 if ((nclear = data_vlimit - clear_vaddr) > 0) { 225 /* Make sure the end of the segment is writable */ 226 if ((data_prot & PROT_WRITE) == 0 && 227 -1 == mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) { 228 _rtld_error("%s: mprotect failed: %s", path, 229 strerror(errno)); 230 return NULL; 231 } 232 233 memset(clear_addr, 0, nclear); 234 235 /* Reset the data protection back */ 236 if ((data_prot & PROT_WRITE) == 0) 237 mprotect(clear_page, PAGE_SIZE, data_prot); 238 } 239 240 /* Overlay the BSS segment onto the proper region. */ 241 bss_vaddr = data_vlimit; 242 bss_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_memsz); 243 bss_addr = mapbase + (bss_vaddr - base_vaddr); 244 if (bss_vlimit > bss_vaddr) { /* There is something to do */ 245 if (mmap(bss_addr, bss_vlimit - bss_vaddr, data_prot, 246 MAP_PRIVATE|MAP_FIXED|MAP_ANON, -1, 0) == (caddr_t) -1) { 247 _rtld_error("%s: mmap of bss failed: %s", path, 248 strerror(errno)); 249 return NULL; 250 } 251 } 252 } 253 254 obj = obj_new(); 255 if (sb != NULL) { 256 obj->dev = sb->st_dev; 257 obj->ino = sb->st_ino; 258 } 259 obj->mapbase = mapbase; 260 obj->mapsize = mapsize; 261 obj->textsize = round_page(segs[0]->p_vaddr + segs[0]->p_memsz) - 262 base_vaddr; 263 obj->vaddrbase = base_vaddr; 264 obj->relocbase = mapbase - base_vaddr; 265 obj->dynamic = (const Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr); 266 if (u.hdr.e_entry != 0) 267 obj->entry = (caddr_t) (obj->relocbase + u.hdr.e_entry); 268 if (phphdr != NULL) { 269 obj->phdr = (const Elf_Phdr *) (obj->relocbase + phphdr->p_vaddr); 270 obj->phsize = phphdr->p_memsz; 271 } 272 if (phinterp != NULL) 273 obj->interp = (const char *) (obj->relocbase + phinterp->p_vaddr); 274 275 return obj; 276 } 277 278 void 279 obj_free(Obj_Entry *obj) 280 { 281 Objlist_Entry *elm; 282 283 free(obj->path); 284 while (obj->needed != NULL) { 285 Needed_Entry *needed = obj->needed; 286 obj->needed = needed->next; 287 free(needed); 288 } 289 while (!STAILQ_EMPTY(&obj->dldags)) { 290 elm = STAILQ_FIRST(&obj->dldags); 291 STAILQ_REMOVE_HEAD(&obj->dldags, link); 292 free(elm); 293 } 294 while (!STAILQ_EMPTY(&obj->dagmembers)) { 295 elm = STAILQ_FIRST(&obj->dagmembers); 296 STAILQ_REMOVE_HEAD(&obj->dagmembers, link); 297 free(elm); 298 } 299 free(obj); 300 } 301 302 Obj_Entry * 303 obj_new(void) 304 { 305 Obj_Entry *obj; 306 307 obj = CNEW(Obj_Entry); 308 STAILQ_INIT(&obj->dldags); 309 STAILQ_INIT(&obj->dagmembers); 310 return obj; 311 } 312 313 /* 314 * Given a set of ELF protection flags, return the corresponding protection 315 * flags for MMAP. 316 */ 317 static int 318 protflags(int elfflags) 319 { 320 int prot = 0; 321 if (elfflags & PF_R) 322 prot |= PROT_READ; 323 if (elfflags & PF_W) 324 prot |= PROT_WRITE; 325 if (elfflags & PF_X) 326 prot |= PROT_EXEC; 327 return prot; 328 } 329