1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 30 #include <link.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <sys/regset.h> 36 #include <sys/frame.h> 37 #include <sys/lwp.h> 38 #include <fcntl.h> 39 #include <stdio.h> 40 #include <sys/mman.h> 41 #include <errno.h> 42 #include <signal.h> 43 #include <synch.h> 44 #include <string.h> 45 46 #include "bindings.h" 47 #include "env.h" 48 49 static Elist *bindto_list = 0; 50 static Elist *bindfrom_list = 0; 51 52 static bindhead *bhp = NULL; 53 static unsigned int current_map_len = 0; 54 static char *buffer_name; 55 static const sigset_t iset = { ~0U, ~0U, ~0U, ~0U }; 56 static lwp_mutex_t sharedmutex = SHAREDMUTEX; 57 58 /* 59 * This routine was stolen from libelf.so.1 60 */ 61 static unsigned long 62 ehash(const char *name) 63 { 64 register unsigned int g, h = 0; 65 register const unsigned char *nm = (unsigned char *)name; 66 67 while (*nm != '\0') { 68 h = (h << 4) + *nm++; 69 /* LINTED */ 70 if ((g = (unsigned int)(h & MASK)) != 0) 71 h ^= g >> 24; 72 h &= ~MASK; 73 } 74 return ((unsigned long)h); 75 } 76 77 78 static void 79 output_err_message(const char *msg) 80 { 81 int fd; 82 if ((fd = open("/tmp/bind_err", O_RDWR | O_CREAT, 0666)) == -1) { 83 (void) fprintf(stderr, "bindings.so: unable to open err_log\n"); 84 perror("open"); 85 } 86 (void) lseek(fd, 0, SEEK_END); 87 (void) write(fd, msg, strlen(msg)); 88 (void) close(fd); 89 } 90 91 /* 92 * common mutex locking & unlocking routines for this module. This is to 93 * control the setting of 'lock_held'. 94 */ 95 static void 96 bt_lock(lwp_mutex_t *lock) 97 { 98 if (_lwp_mutex_lock(lock) != 0) { 99 output_err_message("bt_lock failed!!\n"); 100 (void) fprintf(stderr, "bindings.so: unable to obtain lock\n"); 101 perror("_lwp_mutex_lock"); 102 } 103 } 104 105 static void 106 bt_unlock(lwp_mutex_t *lock) 107 { 108 if (_lwp_mutex_unlock(lock) != 0) { 109 output_err_message("bt_unlock failed!!\n"); 110 (void) fprintf(stderr, "bindings.so: unable to unlock lock\n"); 111 perror("_lwp_mutex_unlock"); 112 } 113 } 114 115 116 117 /* 118 * It's always possible that another process sharing our buffer 119 * has caused it to grow. If this is the case we must adjust our 120 * mappings to compensate. 121 */ 122 static void 123 remap_buffer(int fd) 124 { 125 void * new_bhp; 126 if ((new_bhp = mmap(0, bhp->bh_size, PROT_READ | PROT_WRITE, 127 MAP_SHARED, fd, 0)) == MAP_FAILED) { 128 (void) fprintf(stderr, "bindings: remap: mmap failed\n"); 129 perror("mmap"); 130 131 bt_unlock(&bhp->bh_lock); 132 exit(1); 133 } 134 /* 135 * clean up old mapping 136 */ 137 (void) munmap((caddr_t)bhp, current_map_len); 138 bhp = (bindhead *)new_bhp; 139 current_map_len = bhp->bh_size; 140 } 141 142 static void 143 grow_buffer(void) 144 { 145 int fd; 146 if ((fd = open(buffer_name, O_RDWR)) == -1) { 147 (void) fprintf(stderr, 148 "bidings: grow_buffer: open failed: %s\n", 149 buffer_name); 150 perror("open"); 151 bt_unlock(&bhp->bh_lock); 152 exit(1); 153 } 154 if (ftruncate(fd, bhp->bh_size + BLKSIZE) == -1) { 155 (void) fprintf(stderr, "grow_buffer failed\n"); 156 perror("ftruncate"); 157 bt_unlock(&bhp->bh_lock); 158 exit(1); 159 } 160 bhp->bh_size += BLKSIZE; 161 remap_buffer(fd); 162 (void) close(fd); 163 } 164 165 static void 166 get_new_strbuf(void) 167 { 168 bt_lock(&bhp->bh_lock); 169 while (bhp->bh_end + STRBLKSIZE > bhp->bh_size) 170 grow_buffer(); 171 172 bhp->bh_strcur = bhp->bh_end; 173 bhp->bh_end = bhp->bh_strend = bhp->bh_strcur + STRBLKSIZE; 174 bt_unlock(&bhp->bh_lock); 175 } 176 177 static unsigned int 178 save_str(const char *str) 179 { 180 char *sptr; 181 unsigned int bptr; 182 unsigned int slen; 183 184 bt_lock(&bhp->bh_strlock); 185 /* LINTED */ 186 slen = (unsigned int)strlen(str); 187 188 /* 189 * will string fit into our current string buffer? 190 */ 191 if ((slen + 1) > (bhp->bh_strend - bhp->bh_strcur)) 192 get_new_strbuf(); 193 bptr = bhp->bh_strcur; 194 sptr = (char *)bhp + bhp->bh_strcur; 195 bhp->bh_strcur += slen + 1; 196 (void) strncpy(sptr, str, slen); 197 sptr[slen] = '\0'; 198 bt_unlock(&bhp->bh_strlock); 199 return (bptr); 200 } 201 202 203 static unsigned int 204 get_new_entry(void) 205 { 206 unsigned int new_ent; 207 bt_lock(&bhp->bh_lock); 208 while ((sizeof (binding_entry) + bhp->bh_end) > bhp->bh_size) 209 grow_buffer(); 210 new_ent = bhp->bh_end; 211 bhp->bh_end += sizeof (binding_entry); 212 bt_unlock(&bhp->bh_lock); 213 return (new_ent); 214 } 215 216 217 218 static void 219 init_locks(void) 220 { 221 int i; 222 223 (void) memcpy(&bhp->bh_lock, &sharedmutex, sizeof (lwp_mutex_t)); 224 for (i = 0; i < DEFBKTS; i++) 225 (void) memcpy(&bhp->bh_bkts[i].bb_lock, &sharedmutex, 226 sizeof (lwp_mutex_t)); 227 228 (void) memcpy(&bhp->bh_strlock, &sharedmutex, sizeof (lwp_mutex_t)); 229 } 230 231 uint_t 232 la_version(uint_t version) 233 { 234 int fd; 235 sigset_t omask; 236 237 if (version < LAV_CURRENT) { 238 (void) fprintf(stderr, 239 "bindings.so: unexpected link_audit version: %d\n", 240 version); 241 return (0); 242 } 243 244 build_env_list(&bindto_list, (const char *)"BT_BINDTO"); 245 build_env_list(&bindfrom_list, (const char *)"BT_BINDFROM"); 246 247 if ((buffer_name = getenv(FILEENV)) == NULL) 248 buffer_name = DEFFILE; 249 250 (void) sigprocmask(SIG_BLOCK, &iset, &omask); 251 if ((fd = open(buffer_name, O_RDWR | O_CREAT | O_EXCL, 0666)) != -1) { 252 int init_size = sizeof (bindhead) + BLKSIZE; 253 if (ftruncate(fd, init_size) == -1) { 254 perror("ftruncate"); 255 return (0); 256 } 257 258 /* LINTED */ 259 if ((bhp = (bindhead *)mmap(0, init_size, 260 PROT_READ | PROT_WRITE, 261 MAP_SHARED, fd, 0)) == MAP_FAILED) { 262 perror("bindings.so: mmap"); 263 return (0); 264 } 265 266 (void) close(fd); 267 268 init_locks(); 269 /* 270 * Lock our structure and then initialize the data 271 */ 272 bt_lock(&bhp->bh_lock); 273 bhp->bh_vers = BINDCURVERS; 274 current_map_len = bhp->bh_size = init_size; 275 bhp->bh_end = sizeof (bindhead); 276 bhp->bh_bktcnt = DEFBKTS; 277 bt_unlock(&bhp->bh_lock); 278 /* 279 * Set up our initial string buffer 280 */ 281 get_new_strbuf(); 282 } else if ((fd = open(buffer_name, O_RDWR)) != -1) { 283 struct stat stbuf; 284 int i; 285 for (i = 0; i < 4; i++) { 286 if (fstat(fd, &stbuf) == -1) { 287 (void) sleep(1); 288 continue; 289 } 290 if (stbuf.st_size < sizeof (bindhead)) { 291 (void) sleep(1); 292 continue; 293 } 294 /* LINTED */ 295 if ((bhp = (bindhead *)mmap(0, stbuf.st_size, 296 PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == 297 MAP_FAILED) { 298 (void) fprintf(stderr, 299 "bindings: mmap failed\n"); 300 perror("mmap"); 301 return (0); 302 } 303 304 /* LINTED */ 305 current_map_len = (unsigned int)stbuf.st_size; 306 } 307 if (bhp == NULL) { 308 (void) fprintf(stderr, 309 "bindings: buffer mapping timed out\n"); 310 return (0); 311 } 312 for (i = 0; i < 4; i++) { 313 if (bhp->bh_vers == 0) { 314 (void) sleep(1); 315 continue; 316 } 317 } 318 if (bhp->bh_vers == 0) { 319 (void) fprintf(stderr, 320 "bindings: %s not initialized\n", buffer_name); 321 return (0); 322 } 323 324 bt_lock(&bhp->bh_lock); 325 326 if (bhp->bh_size != current_map_len) 327 remap_buffer(fd); 328 (void) close(fd); 329 } else { 330 (void) fprintf(stderr, "bindings: unable to open %s\n", 331 buffer_name); 332 perror("open"); 333 return (0); 334 } 335 336 (void) sigprocmask(SIG_SETMASK, &omask, NULL); 337 bt_unlock(&bhp->bh_lock); 338 339 return (LAV_CURRENT); 340 } 341 342 /* ARGSUSED 0 */ 343 uint_t 344 la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie) 345 { 346 uint_t flags; 347 348 if ((bindto_list == 0) || 349 (check_list(bindto_list, lmp->l_name))) 350 flags = LA_FLG_BINDTO; 351 else 352 flags = 0; 353 354 if ((bindfrom_list == 0) || 355 (check_list(bindfrom_list, lmp->l_name))) 356 flags |= LA_FLG_BINDFROM; 357 358 return (flags); 359 } 360 361 362 /* ARGSUSED 1 */ 363 #if defined(__sparcv9) 364 uintptr_t 365 la_sparcv9_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcooke, 366 uintptr_t *defcook, La_sparcv9_regs *regset, uint_t *sb_flags, 367 const char *sym_name) 368 #elif defined(__sparc) 369 uintptr_t 370 la_sparcv8_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcooke, 371 uintptr_t *defcook, La_sparcv8_regs *regset, uint_t *sb_flags) 372 #elif defined(__amd64) 373 uintptr_t 374 la_amd64_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcooke, 375 uintptr_t *defcook, La_amd64_regs *regset, uint_t *sb_flags, 376 const char *sym_name) 377 #elif defined(__i386) 378 uintptr_t 379 la_i86_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcooke, 380 uintptr_t *defcook, La_i86_regs *regset, uint_t *sb_flags) 381 #endif 382 { 383 unsigned long bktno; 384 Link_map *dlmp = (Link_map *)*defcook; 385 const char *lib_name; 386 sigset_t omask; 387 #if !defined(_LP64) 388 const char *sym_name = (const char *)symp->st_name; 389 #endif 390 391 392 lib_name = dlmp->l_name; 393 394 (void) sigprocmask(SIG_BLOCK, &iset, &omask); 395 if (sym_name == 0) { 396 output_err_message("null symname\n"); 397 return (symp->st_value); 398 } 399 400 bktno = ehash(sym_name) % bhp->bh_bktcnt; 401 402 bt_lock(&bhp->bh_bkts[bktno].bb_lock); 403 404 /* 405 * The buffer has been grown (by another process) and 406 * we need to remap it into memory. 407 */ 408 if (bhp->bh_size != current_map_len) { 409 int fd; 410 if ((fd = open(buffer_name, O_RDWR)) == -1) { 411 (void) fprintf(stderr, 412 "bidings: plt_enter: open failed: %s\n", 413 buffer_name); 414 perror("open"); 415 bt_unlock(&bhp->bh_lock); 416 exit(1); 417 } 418 bt_lock(&bhp->bh_lock); 419 remap_buffer(fd); 420 bt_unlock(&bhp->bh_lock); 421 (void) close(fd); 422 } 423 424 if (bhp->bh_bkts[bktno].bb_head == NULL) { 425 binding_entry * bep; 426 unsigned int be_off; 427 unsigned int sym_off; 428 unsigned int lib_off; 429 430 be_off = get_new_entry(); 431 sym_off = save_str(sym_name); 432 lib_off = save_str(lib_name); 433 /* LINTED */ 434 bep = (binding_entry *)((char *)bhp + be_off); 435 bep->be_next = 0; 436 bep->be_sym_name = sym_off; 437 bep->be_lib_name = lib_off; 438 bep->be_count = 1; 439 bhp->bh_bkts[bktno].bb_head = be_off; 440 } else { 441 int strcmp_res; 442 unsigned int prev_off = 0; 443 binding_entry * prev_bep = NULL; 444 unsigned int cur_off; 445 binding_entry * cur_bep; 446 unsigned int lib_off = 0; 447 448 /* 449 * Once we get to the bucket, we do a two tiered 450 * search. First we search for a library match, then 451 * we search for a symbol match. 452 */ 453 cur_off = bhp->bh_bkts[bktno].bb_head; 454 /* LINTED */ 455 cur_bep = (binding_entry *)((char *)bhp + 456 cur_off); 457 while (cur_off && (strcmp_res = strcmp((char *)bhp + 458 cur_bep->be_lib_name, lib_name)) < 0) { 459 prev_off = cur_off; 460 cur_off = cur_bep->be_next; 461 /* LINTED */ 462 cur_bep = (binding_entry *)((char *)bhp + 463 cur_off); 464 } 465 if (cur_off && (strcmp_res == 0)) { 466 /* 467 * This is a small optimization. For 468 * each bucket we will only record a library 469 * name once. Once it has been recorded in 470 * a bucket we will just re-use the same 471 * string. 472 */ 473 lib_off = cur_bep->be_lib_name; 474 while (cur_off && (strcmp_res = strcmp((char *)bhp + 475 cur_bep->be_sym_name, sym_name)) < 0) { 476 prev_off = cur_off; 477 cur_off = cur_bep->be_next; 478 /* LINTED */ 479 cur_bep = (binding_entry *)((char *)bhp + 480 cur_off); 481 } 482 } 483 if (strcmp_res == 0) { 484 /* 485 * We've got a match 486 */ 487 cur_bep->be_count++; 488 } else { 489 unsigned int new_off; 490 binding_entry * new_bep; 491 unsigned int sym_off; 492 493 new_off = get_new_entry(); 494 if (lib_off == 0) 495 lib_off = save_str(lib_name); 496 sym_off = save_str(sym_name); 497 498 /* LINTED */ 499 new_bep = (binding_entry *)((char *)bhp + 500 new_off); 501 new_bep->be_sym_name = sym_off; 502 new_bep->be_lib_name = lib_off; 503 new_bep->be_count = 1; 504 new_bep->be_next = cur_off; 505 if (prev_off) { 506 /* LINTED */ 507 prev_bep = (binding_entry *)((char *)bhp + 508 prev_off); 509 prev_bep->be_next = new_off; 510 } else 511 /* 512 * Insert at head of list. 513 */ 514 bhp->bh_bkts[bktno].bb_head = new_off; 515 516 } 517 } 518 bt_unlock(&bhp->bh_bkts[bktno].bb_lock); 519 (void) sigprocmask(SIG_SETMASK, &omask, NULL); 520 return (symp->st_value); 521 } 522