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