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