1 /* $OpenBSD: library.c,v 1.58 2008/10/02 20:12:08 kurt 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 42 #define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \ 43 (((X) & PF_W) ? PROT_WRITE : 0) | \ 44 (((X) & PF_X) ? PROT_EXEC : 0)) 45 46 void 47 _dl_load_list_free(struct load_list *load_list) 48 { 49 struct load_list *next; 50 51 while (load_list != NULL) { 52 next = load_list->next; 53 _dl_free(load_list); 54 load_list = next; 55 } 56 } 57 58 void 59 _dl_unload_shlib(elf_object_t *object) 60 { 61 struct dep_node *n; 62 DL_DEB(("unload_shlib called on %s\n", object->load_name)); 63 if (OBJECT_REF_CNT(object) == 0 && 64 (object->status & STAT_UNLOADED) == 0) { 65 object->status |= STAT_UNLOADED; 66 TAILQ_FOREACH(n, &object->child_list, next_sib) 67 _dl_unload_shlib(n->data); 68 TAILQ_FOREACH(n, &object->grpref_list, next_sib) 69 _dl_unload_shlib(n->data); 70 DL_DEB(("unload_shlib unloading on %s\n", object->load_name)); 71 _dl_load_list_free(object->load_list); 72 _dl_munmap((void *)object->load_base, object->load_size); 73 _dl_remove_object(object); 74 } 75 } 76 77 elf_object_t * 78 _dl_tryload_shlib(const char *libname, int type, int flags) 79 { 80 int libfile, i; 81 struct load_list *next_load, *load_list = NULL; 82 Elf_Addr maxva = 0, minva = ELFDEFNNAME(NO_ADDR); 83 Elf_Addr libaddr, loff, align = _dl_pagesz - 1; 84 elf_object_t *object; 85 char hbuf[4096]; 86 Elf_Dyn *dynp = 0; 87 Elf_Ehdr *ehdr; 88 Elf_Phdr *phdp; 89 struct stat sb; 90 void *prebind_data; 91 92 #define ROUND_PG(x) (((x) + align) & ~(align)) 93 #define TRUNC_PG(x) ((x) & ~(align)) 94 95 object = _dl_lookup_object(libname); 96 if (object) { 97 object->obj_flags |= flags & RTLD_GLOBAL; 98 if (_dl_loading_object == NULL) 99 _dl_loading_object = object; 100 if (object->load_object != _dl_objects && 101 object->load_object != _dl_loading_object) { 102 _dl_link_grpref(object->load_object, _dl_loading_object); 103 } 104 return(object); /* Already loaded */ 105 } 106 107 libfile = _dl_open(libname, O_RDONLY); 108 if (libfile < 0) { 109 _dl_errno = DL_CANT_OPEN; 110 return(0); 111 } 112 113 if ( _dl_fstat(libfile, &sb) < 0) { 114 _dl_errno = DL_CANT_OPEN; 115 return(0); 116 } 117 118 for (object = _dl_objects; object != NULL; object = object->next) { 119 if (object->dev == sb.st_dev && 120 object->inode == sb.st_ino) { 121 object->obj_flags |= flags & RTLD_GLOBAL; 122 _dl_close(libfile); 123 if (_dl_loading_object == NULL) 124 _dl_loading_object = object; 125 if (object->load_object != _dl_objects && 126 object->load_object != _dl_loading_object) { 127 _dl_link_grpref(object->load_object, 128 _dl_loading_object); 129 } 130 return(object); 131 } 132 } 133 134 _dl_read(libfile, hbuf, sizeof(hbuf)); 135 ehdr = (Elf_Ehdr *)hbuf; 136 if (ehdr->e_ident[0] != ELFMAG0 || ehdr->e_ident[1] != ELFMAG1 || 137 ehdr->e_ident[2] != ELFMAG2 || ehdr->e_ident[3] != ELFMAG3 || 138 ehdr->e_type != ET_DYN || ehdr->e_machine != MACHID) { 139 _dl_close(libfile); 140 _dl_errno = DL_NOT_ELF; 141 return(0); 142 } 143 144 /* 145 * Alright, we might have a winner! 146 * Figure out how much VM space we need. 147 */ 148 phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); 149 for (i = 0; i < ehdr->e_phnum; i++, phdp++) { 150 switch (phdp->p_type) { 151 case PT_LOAD: 152 if (phdp->p_vaddr < minva) 153 minva = phdp->p_vaddr; 154 if (phdp->p_vaddr + phdp->p_memsz > maxva) 155 maxva = phdp->p_vaddr + phdp->p_memsz; 156 break; 157 case PT_DYNAMIC: 158 dynp = (Elf_Dyn *)phdp->p_vaddr; 159 break; 160 default: 161 break; 162 } 163 } 164 minva = TRUNC_PG(minva); 165 maxva = ROUND_PG(maxva); 166 167 /* 168 * We map the entire area to see that we can get the VM 169 * space required. Map it unaccessible to start with. 170 * 171 * We must map the file we'll map later otherwise the VM 172 * system won't be able to align the mapping properly 173 * on VAC architectures. 174 */ 175 libaddr = (Elf_Addr)_dl_mmap(0, maxva - minva, PROT_NONE, 176 MAP_PRIVATE|MAP_FILE, libfile, 0); 177 if (_dl_mmap_error(libaddr)) { 178 _dl_printf("%s: rtld mmap failed mapping %s.\n", 179 _dl_progname, libname); 180 _dl_close(libfile); 181 _dl_errno = DL_CANT_MMAP; 182 return(0); 183 } 184 185 loff = libaddr - minva; 186 phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); 187 188 for (i = 0; i < ehdr->e_phnum; i++, phdp++) { 189 if (phdp->p_type == PT_LOAD) { 190 char *start = (char *)(TRUNC_PG(phdp->p_vaddr)) + loff; 191 Elf_Addr off = (phdp->p_vaddr & align); 192 Elf_Addr size = off + phdp->p_filesz; 193 void *res; 194 195 res = _dl_mmap(start, ROUND_PG(size), 196 PFLAGS(phdp->p_flags), 197 MAP_FIXED|MAP_PRIVATE, libfile, 198 TRUNC_PG(phdp->p_offset)); 199 next_load = _dl_malloc(sizeof(struct load_list)); 200 next_load->next = load_list; 201 load_list = next_load; 202 next_load->start = start; 203 next_load->size = size; 204 next_load->prot = PFLAGS(phdp->p_flags); 205 if (_dl_mmap_error(res)) { 206 _dl_printf("%s: rtld mmap failed mapping %s.\n", 207 _dl_progname, libname); 208 _dl_close(libfile); 209 _dl_errno = DL_CANT_MMAP; 210 _dl_munmap((void *)libaddr, maxva - minva); 211 _dl_load_list_free(load_list); 212 return(0); 213 } 214 if (phdp->p_flags & PF_W) { 215 /* Zero out everything past the EOF */ 216 if ((size & align) != 0) 217 _dl_memset(start + size, 0, 218 _dl_pagesz - (size & align)); 219 if (ROUND_PG(size) == 220 ROUND_PG(off + phdp->p_memsz)) 221 continue; 222 start = start + ROUND_PG(size); 223 size = ROUND_PG(off + phdp->p_memsz) - 224 ROUND_PG(size); 225 res = _dl_mmap(start, size, 226 PFLAGS(phdp->p_flags), 227 MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); 228 if (_dl_mmap_error(res)) { 229 _dl_printf("%s: rtld mmap failed mapping %s.\n", 230 _dl_progname, libname); 231 _dl_close(libfile); 232 _dl_errno = DL_CANT_MMAP; 233 _dl_munmap((void *)libaddr, maxva - minva); 234 _dl_load_list_free(load_list); 235 return(0); 236 } 237 } 238 } 239 } 240 241 prebind_data = prebind_load_fd(libfile, libname); 242 243 _dl_close(libfile); 244 245 dynp = (Elf_Dyn *)((unsigned long)dynp + loff); 246 object = _dl_finalize_object(libname, dynp, 247 (Elf_Phdr *)((char *)libaddr + ehdr->e_phoff), ehdr->e_phnum,type, 248 libaddr, loff); 249 if (object) { 250 object->prebind_data = prebind_data; 251 object->load_size = maxva - minva; /*XXX*/ 252 object->load_list = load_list; 253 /* set inode, dev from stat info */ 254 object->dev = sb.st_dev; 255 object->inode = sb.st_ino; 256 object->obj_flags |= flags; 257 } else { 258 /* XXX not possible. object cannot come back NULL */ 259 _dl_munmap((void *)libaddr, maxva - minva); 260 _dl_load_list_free(load_list); 261 } 262 return(object); 263 } 264