1 /* $OpenBSD: library.c,v 1.62 2011/05/10 04:50:35 otto Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Dale Rahn 5 * Copyright (c) 1998 Per Fogelstrom, Opsycon AB 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #define _DYN_LOADER 31 32 #include <sys/types.h> 33 #include <sys/param.h> 34 #include <fcntl.h> 35 #include <sys/mman.h> 36 #include "dl_prebind.h" 37 38 #include "syscall.h" 39 #include "archdep.h" 40 #include "resolve.h" 41 #include "sod.h" 42 43 #define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \ 44 (((X) & PF_W) ? PROT_WRITE : 0) | \ 45 (((X) & PF_X) ? PROT_EXEC : 0)) 46 47 void 48 _dl_load_list_free(struct load_list *load_list) 49 { 50 struct load_list *next; 51 52 while (load_list != NULL) { 53 next = load_list->next; 54 _dl_free(load_list); 55 load_list = next; 56 } 57 } 58 59 void 60 _dl_unload_shlib(elf_object_t *object) 61 { 62 struct dep_node *n; 63 DL_DEB(("unload_shlib called on %s\n", object->load_name)); 64 if (OBJECT_REF_CNT(object) == 0 && 65 (object->status & STAT_UNLOADED) == 0) { 66 object->status |= STAT_UNLOADED; 67 TAILQ_FOREACH(n, &object->child_list, next_sib) 68 _dl_unload_shlib(n->data); 69 TAILQ_FOREACH(n, &object->grpref_list, next_sib) 70 _dl_unload_shlib(n->data); 71 DL_DEB(("unload_shlib unloading on %s\n", object->load_name)); 72 _dl_load_list_free(object->load_list); 73 _dl_munmap((void *)object->load_base, object->load_size); 74 _dl_remove_object(object); 75 } 76 } 77 78 elf_object_t * 79 _dl_tryload_shlib(const char *libname, int type, int flags) 80 { 81 int libfile, i; 82 struct load_list *next_load, *load_list = NULL; 83 Elf_Addr maxva = 0, minva = ELFDEFNNAME(NO_ADDR); 84 Elf_Addr libaddr, loff, align = _dl_pagesz - 1; 85 elf_object_t *object; 86 char hbuf[4096]; 87 Elf_Dyn *dynp = 0; 88 Elf_Ehdr *ehdr; 89 Elf_Phdr *phdp; 90 struct stat sb; 91 void *prebind_data; 92 93 #define ROUND_PG(x) (((x) + align) & ~(align)) 94 #define TRUNC_PG(x) ((x) & ~(align)) 95 96 libfile = _dl_open(libname, O_RDONLY); 97 if (libfile < 0) { 98 _dl_errno = DL_CANT_OPEN; 99 return(0); 100 } 101 102 if ( _dl_fstat(libfile, &sb) < 0) { 103 _dl_errno = DL_CANT_OPEN; 104 return(0); 105 } 106 107 for (object = _dl_objects; object != NULL; object = object->next) { 108 if (object->dev == sb.st_dev && 109 object->inode == sb.st_ino) { 110 object->obj_flags |= flags & RTLD_GLOBAL; 111 _dl_close(libfile); 112 if (_dl_loading_object == NULL) 113 _dl_loading_object = object; 114 if (object->load_object != _dl_objects && 115 object->load_object != _dl_loading_object) { 116 _dl_link_grpref(object->load_object, 117 _dl_loading_object); 118 } 119 return(object); 120 } 121 } 122 123 _dl_read(libfile, hbuf, sizeof(hbuf)); 124 ehdr = (Elf_Ehdr *)hbuf; 125 if (ehdr->e_ident[0] != ELFMAG0 || ehdr->e_ident[1] != ELFMAG1 || 126 ehdr->e_ident[2] != ELFMAG2 || ehdr->e_ident[3] != ELFMAG3 || 127 ehdr->e_type != ET_DYN || ehdr->e_machine != MACHID) { 128 _dl_close(libfile); 129 _dl_errno = DL_NOT_ELF; 130 return(0); 131 } 132 133 /* 134 * Alright, we might have a winner! 135 * Figure out how much VM space we need. 136 */ 137 phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); 138 for (i = 0; i < ehdr->e_phnum; i++, phdp++) { 139 switch (phdp->p_type) { 140 case PT_LOAD: 141 if (phdp->p_vaddr < minva) 142 minva = phdp->p_vaddr; 143 if (phdp->p_vaddr + phdp->p_memsz > maxva) 144 maxva = phdp->p_vaddr + phdp->p_memsz; 145 break; 146 case PT_DYNAMIC: 147 dynp = (Elf_Dyn *)phdp->p_vaddr; 148 break; 149 default: 150 break; 151 } 152 } 153 minva = TRUNC_PG(minva); 154 maxva = ROUND_PG(maxva); 155 156 /* 157 * We map the entire area to see that we can get the VM 158 * space required. Map it unaccessible to start with. 159 * 160 * We must map the file we'll map later otherwise the VM 161 * system won't be able to align the mapping properly 162 * on VAC architectures. 163 */ 164 libaddr = (Elf_Addr)_dl_mmap(0, maxva - minva, PROT_NONE, 165 MAP_PRIVATE|MAP_FILE, libfile, 0); 166 if (_dl_mmap_error(libaddr)) { 167 _dl_printf("%s: rtld mmap failed mapping %s.\n", 168 _dl_progname, libname); 169 _dl_close(libfile); 170 _dl_errno = DL_CANT_MMAP; 171 return(0); 172 } 173 174 loff = libaddr - minva; 175 phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); 176 177 for (i = 0; i < ehdr->e_phnum; i++, phdp++) { 178 if (phdp->p_type == PT_LOAD) { 179 char *start = (char *)(TRUNC_PG(phdp->p_vaddr)) + loff; 180 Elf_Addr off = (phdp->p_vaddr & align); 181 Elf_Addr size = off + phdp->p_filesz; 182 void *res; 183 184 res = _dl_mmap(start, ROUND_PG(size), 185 PFLAGS(phdp->p_flags), 186 MAP_FIXED|MAP_PRIVATE, libfile, 187 TRUNC_PG(phdp->p_offset)); 188 next_load = _dl_malloc(sizeof(struct load_list)); 189 next_load->next = load_list; 190 load_list = next_load; 191 next_load->start = start; 192 next_load->size = size; 193 next_load->prot = PFLAGS(phdp->p_flags); 194 if (_dl_mmap_error(res)) { 195 _dl_printf("%s: rtld mmap failed mapping %s.\n", 196 _dl_progname, libname); 197 _dl_close(libfile); 198 _dl_errno = DL_CANT_MMAP; 199 _dl_munmap((void *)libaddr, maxva - minva); 200 _dl_load_list_free(load_list); 201 return(0); 202 } 203 if (phdp->p_flags & PF_W) { 204 /* Zero out everything past the EOF */ 205 if ((size & align) != 0) 206 _dl_memset(start + size, 0, 207 _dl_pagesz - (size & align)); 208 if (ROUND_PG(size) == 209 ROUND_PG(off + phdp->p_memsz)) 210 continue; 211 start = start + ROUND_PG(size); 212 size = ROUND_PG(off + phdp->p_memsz) - 213 ROUND_PG(size); 214 res = _dl_mmap(start, size, 215 PFLAGS(phdp->p_flags), 216 MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); 217 if (_dl_mmap_error(res)) { 218 _dl_printf("%s: rtld mmap failed mapping %s.\n", 219 _dl_progname, libname); 220 _dl_close(libfile); 221 _dl_errno = DL_CANT_MMAP; 222 _dl_munmap((void *)libaddr, maxva - minva); 223 _dl_load_list_free(load_list); 224 return(0); 225 } 226 } 227 } 228 } 229 230 prebind_data = prebind_load_fd(libfile, libname); 231 232 _dl_close(libfile); 233 234 dynp = (Elf_Dyn *)((unsigned long)dynp + loff); 235 object = _dl_finalize_object(libname, dynp, 236 (Elf_Phdr *)((char *)libaddr + ehdr->e_phoff), ehdr->e_phnum,type, 237 libaddr, loff); 238 if (object) { 239 object->prebind_data = prebind_data; 240 object->load_size = maxva - minva; /*XXX*/ 241 object->load_list = load_list; 242 /* set inode, dev from stat info */ 243 object->dev = sb.st_dev; 244 object->inode = sb.st_ino; 245 object->obj_flags |= flags; 246 _dl_build_sod(object->load_name, &object->sod); 247 } else { 248 /* XXX not possible. object cannot come back NULL */ 249 _dl_munmap((void *)libaddr, maxva - minva); 250 _dl_load_list_free(load_list); 251 } 252 return(object); 253 } 254