1 /* $OpenBSD: library.c,v 1.8 2001/08/06 15:09:58 drahn Exp $ */ 2 3 /* 4 * Copyright (c) 1998 Per Fogelstrom, Opsycon AB 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed under OpenBSD by 17 * Per Fogelstrom, Opsycon AB, Sweden. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 */ 34 35 #define _DYN_LOADER 36 37 #include <sys/types.h> 38 #include <sys/syslimits.h> 39 #include <fcntl.h> 40 #include <nlist.h> 41 #include <link.h> 42 #include <sys/mman.h> 43 44 #include "syscall.h" 45 #include "archdep.h" 46 #include "resolve.h" 47 48 #define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \ 49 (((X) & PF_W) ? PROT_WRITE : 0) | \ 50 (((X) & PF_X) ? PROT_EXEC : 0)) 51 52 elf_object_t * _dl_tryload_shlib(const char *libname, int type); 53 void _dl_build_sod(const char *name, struct sod *sodp); 54 char * _dl_findhint(char *name, int major, int minor, char *prefered_path); 55 56 /* 57 * Load a shared object. Search order is: 58 * If the name contains a '/' use the name exactly as is. 59 * Otherwise first check DT_RPATH paths, 60 * then try the LD_LIBRARY_PATH specification and 61 * last look in /usr/lib. 62 */ 63 64 elf_object_t * 65 _dl_load_shlib(const char *libname, elf_object_t *parent, int type) 66 { 67 char lp[PATH_MAX + 10]; 68 char *path = lp; 69 const char *pp; 70 elf_object_t *object; 71 struct sod sodp; 72 char *hint; 73 74 if(_dl_strchr(libname, '/')) { 75 object = _dl_tryload_shlib(libname, type); 76 return(object); 77 } 78 79 /* 80 * No '/' in name. Scan the known places, LD_LIBRARY_PATH first. 81 */ 82 pp = _dl_libpath; 83 while(pp) { 84 const char *ln = libname; 85 86 path = lp; 87 while(path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';') { 88 *path++ = *pp++; 89 } 90 if(path != lp && *(path - 1) != '/') { /* Insert '/' */ 91 *path++ = '/'; 92 } 93 while(path < lp + PATH_MAX && (*path++ = *ln++)) {}; 94 if(path < lp + PATH_MAX) { 95 object = _dl_tryload_shlib(lp, type); 96 if(object) { 97 return(object); 98 } 99 } 100 if(*pp) { /* Try curdir if ':' at end */ 101 pp++; 102 } 103 else { 104 pp = 0; 105 } 106 } 107 108 /* 109 * Check DT_RPATH. 110 */ 111 pp = parent->dyn.rpath; 112 while(pp) { 113 const char *ln = libname; 114 115 path = lp; 116 while(path < lp + PATH_MAX && *pp && *pp != ':') { 117 *path++ = *pp++; 118 } 119 if(*(path - 1) != '/') {/* Make sure '/' after dir path */ 120 *path++ = '/'; 121 } 122 if(*pp) { /* ':' if not end. skip over. */ 123 pp++; 124 } 125 while(path < lp + PATH_MAX && (*path++ = *ln++)) {}; 126 if(path < lp + PATH_MAX) { 127 object = _dl_tryload_shlib(lp, type); 128 if(object) { 129 return(object); 130 } 131 } 132 if(*pp) { /* Try curdir if ':' at end */ 133 pp++; 134 } 135 else { 136 pp = 0; 137 } 138 } 139 140 _dl_build_sod(libname, &sodp); 141 if ((hint = _dl_findhint((char *)sodp.sod_name, sodp.sod_major, 142 sodp.sod_minor, NULL)) != NULL) 143 { 144 object = _dl_tryload_shlib(hint, type); 145 return(object); 146 147 } 148 149 150 /* 151 * Check '/usr/lib' 152 */ 153 154 _dl_strcpy(lp, "/usr/lib/"); 155 path = lp + sizeof("/usr/lib/") - 1; 156 while(path < lp + PATH_MAX && (*path++ = *libname++)) {}; 157 if(path < lp + PATH_MAX) { 158 object = _dl_tryload_shlib(lp, type); 159 if(object) { 160 return(object); 161 } 162 } 163 _dl_errno = DL_NOT_FOUND; 164 return(0); 165 } 166 167 void 168 _dl_load_list_free(load_list_t *load_list) 169 { 170 load_list_t *next; 171 172 while(load_list != NULL) { 173 next = load_list->next; 174 _dl_free(load_list); 175 load_list = next; 176 } 177 } 178 179 void 180 _dl_unload_shlib(elf_object_t *object) 181 { 182 if(--object->refcount == 0) { 183 _dl_load_list_free(object->load_list); 184 _dl_munmap((void *)object->load_addr, object->load_size); 185 _dl_remove_object(object); 186 } 187 } 188 189 190 elf_object_t * 191 _dl_tryload_shlib(const char *libname, int type) 192 { 193 int libfile; 194 int i; 195 char hbuf[4096]; 196 Elf_Ehdr *ehdr; 197 Elf_Phdr *phdp; 198 Elf_Dyn *dynp = 0; 199 Elf_Addr maxva = 0; 200 Elf_Addr minva = 0x7fffffff; /* XXX Correct for 64bit? */ 201 Elf_Addr libaddr; 202 Elf_Addr loff; 203 int align = _dl_pagesz - 1; 204 elf_object_t *object; 205 load_list_t *next_load, *load_list = NULL; 206 207 object = _dl_lookup_object(libname); 208 if(object) { 209 object->refcount++; 210 return(object); /* Already loaded */ 211 } 212 213 libfile = _dl_open(libname, O_RDONLY); 214 if(libfile < 0) { 215 _dl_errno = DL_CANT_OPEN; 216 return(0); 217 } 218 219 _dl_read(libfile, hbuf, sizeof(hbuf)); 220 ehdr = (Elf_Ehdr *)hbuf; 221 if(_dl_strncmp(ehdr->e_ident, ELFMAG, SELFMAG) || 222 ehdr->e_type != ET_DYN || ehdr->e_machine != MACHID) { 223 _dl_close(libfile); 224 _dl_errno = DL_NOT_ELF; 225 return(0); 226 } 227 228 /* 229 * Alright, we might have a winner! 230 * Figure out how much VM space we need. 231 */ 232 233 phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); 234 for(i = 0; i < ehdr->e_phnum; i++, phdp++) { 235 switch(phdp->p_type) { 236 case PT_LOAD: 237 if(phdp->p_vaddr < minva) { 238 minva = phdp->p_vaddr; 239 } 240 if(phdp->p_vaddr + phdp->p_memsz > maxva) { 241 maxva = phdp->p_vaddr + phdp->p_memsz; 242 } 243 break; 244 245 case PT_DYNAMIC: 246 dynp = (Elf_Dyn *)phdp->p_vaddr; 247 break; 248 249 default: 250 break; 251 } 252 } 253 minva &= ~align; 254 maxva = (maxva + align) & ~(align); 255 256 /* 257 * We map the entire area to see that we can get the VM 258 * space required. Map it unaccessible to start with. 259 */ 260 libaddr = (Elf_Addr)_dl_mmap(0, maxva - minva, PROT_NONE, 261 MAP_PRIVATE|MAP_ANON, -1, 0); 262 if(_dl_check_error(libaddr)) { 263 _dl_printf("%s: rtld mmap failed mapping %s.\n", 264 _dl_progname, libname); 265 _dl_close(libfile); 266 _dl_errno = DL_CANT_MMAP; 267 return(0); 268 } 269 270 loff = libaddr - minva; 271 phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); 272 273 for(i = 0; i < ehdr->e_phnum; i++, phdp++) { 274 if(phdp->p_type == PT_LOAD) { 275 int res; 276 char *start = (char *)(phdp->p_vaddr & ~align) + loff; 277 int size = (phdp->p_vaddr & align) + phdp->p_filesz; 278 res = _dl_mmap(start, size, PFLAGS(phdp->p_flags), 279 MAP_FIXED|MAP_PRIVATE, libfile, 280 phdp->p_offset & ~align); 281 next_load = (load_list_t *)_dl_malloc( 282 sizeof(load_list_t)); 283 next_load->next = load_list; 284 load_list = next_load; 285 next_load->start = start; 286 next_load->size = size; 287 next_load->prot = PFLAGS(phdp->p_flags); 288 if(_dl_check_error(res)) { 289 _dl_printf("%s: rtld mmap failed mapping %s.\n", 290 _dl_progname, libname); 291 _dl_close(libfile); 292 _dl_errno = DL_CANT_MMAP; 293 _dl_munmap((void *)libaddr, maxva - minva); 294 _dl_load_list_free(load_list); 295 return(0); 296 } 297 if (phdp->p_flags & PF_W) { 298 if(size & align) { 299 _dl_memset(start + size, 0, 300 _dl_pagesz - (size & align)); 301 } 302 start = start + ((size + align) & ~align); 303 size = size - (phdp->p_vaddr & align); 304 size = phdp->p_memsz - size; 305 res = _dl_mmap(start, size, 306 PFLAGS(phdp->p_flags), 307 MAP_FIXED|MAP_PRIVATE|MAP_ANON, 308 -1, 0); 309 if(_dl_check_error(res)) { 310 _dl_printf("%s: rtld mmap failed mapping %s.\n", 311 _dl_progname, libname); 312 _dl_close(libfile); 313 _dl_errno = DL_CANT_MMAP; 314 _dl_munmap((void *)libaddr, maxva - minva); 315 _dl_load_list_free(load_list); 316 return(0); 317 } 318 } 319 } 320 } 321 _dl_close(libfile); 322 323 dynp = (Elf_Dyn *)((unsigned long)dynp + loff); 324 object = _dl_add_object(libname, dynp, 0, type, libaddr, loff); 325 if(object) { 326 object->load_size = maxva - minva; /*XXX*/ 327 object->load_list = load_list; 328 } else { 329 _dl_munmap((void *)libaddr, maxva - minva); 330 _dl_load_list_free(load_list); 331 } 332 return(object); 333 } 334