resolve.c revision 1.70
1/* $OpenBSD: resolve.c,v 1.70 2016/01/24 03:45:54 guenther 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 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29#define _DYN_LOADER 30 31#include <sys/types.h> 32 33#include <limits.h> 34#include <nlist.h> 35#include <link.h> 36#include "syscall.h" 37#include "archdep.h" 38#include "path.h" 39#include "resolve.h" 40#include "dl_prebind.h" 41 42/* substitution types */ 43typedef enum { 44 SUBST_UNKNOWN, SUBST_ORIGIN, SUBST_OSNAME, SUBST_OSREL, SUBST_PLATFORM 45} SUBST_TYPES; 46 47elf_object_t *_dl_objects; 48elf_object_t *_dl_last_object; 49elf_object_t *_dl_loading_object; 50 51/* 52 * Add a new dynamic object to the object list. 53 */ 54void 55_dl_add_object(elf_object_t *object) 56{ 57 /* 58 * If a .so is marked nodelete, then the entire load group that it's 59 * in needs to be kept around forever, so add a reference there. 60 * XXX It would be better if we tracked inter-object dependencies 61 * from relocations and didn't leave dangling pointers when a load 62 * group was partially unloaded. That would render this unnecessary. 63 */ 64 if (object->obj_flags & DF_1_NODELETE && 65 (object->load_object->status & STAT_NODELETE) == 0) { 66 DL_DEB(("objname %s is nodelete\n", object->load_name)); 67 object->load_object->opencount++; 68 object->load_object->status |= STAT_NODELETE; 69 } 70 71 /* 72 * if this is a new object, prev will be NULL 73 * != NULL if an object already in the list 74 * prev == NULL for the first item in the list, but that will 75 * be the executable. 76 */ 77 if (object->prev != NULL) 78 return; 79 80 if (_dl_objects == NULL) { /* First object ? */ 81 _dl_last_object = _dl_objects = object; 82 } else { 83 _dl_last_object->next = object; 84 object->prev = _dl_last_object; 85 _dl_last_object = object; 86 } 87} 88 89/* 90 * Identify substitution sequence name. 91 */ 92static int 93_dl_subst_name(const char *name, size_t siz) { 94 switch (siz) { 95 case 5: 96 if (_dl_strncmp(name, "OSREL", 5) == 0) 97 return SUBST_OSREL; 98 break; 99 case 6: 100 if (_dl_strncmp(name, "ORIGIN", 6) == 0) 101 return SUBST_ORIGIN; 102 if (_dl_strncmp(name, "OSNAME", 6) == 0) 103 return SUBST_OSNAME; 104 break; 105 case 8: 106 if (_dl_strncmp(name, "PLATFORM", 8) == 0) 107 return SUBST_PLATFORM; 108 break; 109 } 110 111 return (SUBST_UNKNOWN); 112} 113 114/* 115 * Perform $ORIGIN substitutions on path 116 */ 117static void 118_dl_origin_subst_path(elf_object_t *object, const char *origin_path, 119 char **path) 120{ 121 char tmp_path[PATH_MAX]; 122 char *new_path, *tp; 123 const char *pp, *name, *value; 124 static struct utsname uts; 125 size_t value_len; 126 int skip_brace; 127 128 if (uts.sysname[0] == '\0') { 129 if (_dl_uname(&uts) != 0) 130 return; 131 } 132 133 tp = tmp_path; 134 pp = *path; 135 136 while (*pp != '\0' && (tp - tmp_path) < sizeof(tmp_path)) { 137 138 /* copy over chars up to but not including $ */ 139 while (*pp != '\0' && *pp != '$' && 140 (tp - tmp_path) < sizeof(tmp_path)) 141 *tp++ = *pp++; 142 143 /* substitution sequence detected */ 144 if (*pp == '$' && (tp - tmp_path) < sizeof(tmp_path)) { 145 pp++; 146 147 if ((skip_brace = (*pp == '{'))) 148 pp++; 149 150 /* skip over name */ 151 name = pp; 152 while (_dl_isalnum((unsigned char)*pp) || *pp == '_') 153 pp++; 154 155 switch (_dl_subst_name(name, pp - name)) { 156 case SUBST_ORIGIN: 157 value = origin_path; 158 break; 159 case SUBST_OSNAME: 160 value = uts.sysname; 161 break; 162 case SUBST_OSREL: 163 value = uts.release; 164 break; 165 case SUBST_PLATFORM: 166 value = uts.machine; 167 break; 168 default: 169 value = ""; 170 } 171 172 value_len = _dl_strlen(value); 173 if (value_len >= sizeof(tmp_path) - (tp - tmp_path)) 174 return; 175 176 _dl_bcopy(value, tp, value_len); 177 tp += value_len; 178 179 if (skip_brace && *pp == '}') 180 pp++; 181 } 182 } 183 184 /* no substitution made if result exceeds sizeof(tmp_path) */ 185 if (tp - tmp_path >= sizeof(tmp_path)) 186 return; 187 188 /* NULL terminate tmp_path */ 189 *tp = '\0'; 190 191 if (_dl_strcmp(tmp_path, *path) == 0) 192 return; 193 194 new_path = _dl_strdup(tmp_path); 195 if (new_path == NULL) 196 return; 197 198 DL_DEB(("orig_path %s\n", *path)); 199 DL_DEB(("new_path %s\n", new_path)); 200 201 _dl_free(*path); 202 *path = new_path; 203} 204 205/* 206 * Determine origin_path from object load_name. The origin_path argument 207 * must refer to a buffer capable of storing at least PATH_MAX characters. 208 * Returns 0 on success. 209 */ 210static int 211_dl_origin_path(elf_object_t *object, char *origin_path) 212{ 213 const char *dirname_path = _dl_dirname(object->load_name); 214 215 if (dirname_path == NULL) 216 return -1; 217 218 if (_dl_realpath(dirname_path, origin_path) == NULL) 219 return -1; 220 221 return 0; 222} 223 224/* 225 * Perform $ORIGIN substitutions on rpath 226 */ 227static void 228_dl_origin_subst(elf_object_t *object) 229{ 230 char origin_path[PATH_MAX]; 231 char **pp; 232 233 if (_dl_origin_path(object, origin_path) != 0) 234 return; 235 236 /* perform path substitutions on each segment of rpath */ 237 for (pp = object->rpath; *pp != NULL; pp++) { 238 _dl_origin_subst_path(object, origin_path, pp); 239 } 240} 241 242/* 243 * Initialize a new dynamic object. 244 */ 245elf_object_t * 246_dl_finalize_object(const char *objname, Elf_Dyn *dynp, Elf_Phdr *phdrp, 247 int phdrc, const int objtype, const long lbase, const long obase) 248{ 249 elf_object_t *object; 250#if 0 251 _dl_printf("objname [%s], dynp %p, objtype %x lbase %lx, obase %lx\n", 252 objname, dynp, objtype, lbase, obase); 253#endif 254 object = _dl_calloc(1, sizeof(elf_object_t)); 255 if (object == NULL) 256 _dl_exit(7); 257 object->prev = object->next = NULL; 258 259 object->load_dyn = dynp; 260 while (dynp->d_tag != DT_NULL) { 261 if (dynp->d_tag < DT_NUM) 262 object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val; 263 else if (dynp->d_tag >= DT_LOPROC && 264 dynp->d_tag < DT_LOPROC + DT_PROCNUM) 265 object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] = 266 dynp->d_un.d_val; 267 if (dynp->d_tag == DT_TEXTREL) 268 object->dyn.textrel = 1; 269 if (dynp->d_tag == DT_SYMBOLIC) 270 object->dyn.symbolic = 1; 271 if (dynp->d_tag == DT_BIND_NOW) 272 object->obj_flags |= DF_1_NOW; 273 if (dynp->d_tag == DT_FLAGS_1) 274 object->obj_flags |= dynp->d_un.d_val; 275 if (dynp->d_tag == DT_RELACOUNT) 276 object->relacount = dynp->d_un.d_val; 277 if (dynp->d_tag == DT_RELCOUNT) 278 object->relcount = dynp->d_un.d_val; 279 dynp++; 280 } 281 DL_DEB((" flags %s = 0x%x\n", objname, object->obj_flags )); 282 object->obj_type = objtype; 283 284 if (_dl_loading_object == NULL) { 285 /* 286 * no loading object, object is the loading object, 287 * as it is either executable, or dlopened() 288 */ 289 _dl_loading_object = object; 290 } 291 292 if ((object->obj_flags & DF_1_NOOPEN) != 0 && 293 _dl_loading_object->obj_type == OBJTYPE_DLO && 294 _dl_traceld == NULL) { 295 _dl_free(object); 296 _dl_errno = DL_CANT_LOAD_OBJ; 297 return(NULL); 298 } 299 300 /* 301 * Now relocate all pointer to dynamic info, but only 302 * the ones which have pointer values. 303 */ 304 if (object->Dyn.info[DT_PLTGOT]) 305 object->Dyn.info[DT_PLTGOT] += obase; 306 if (object->Dyn.info[DT_HASH]) 307 object->Dyn.info[DT_HASH] += obase; 308 if (object->Dyn.info[DT_STRTAB]) 309 object->Dyn.info[DT_STRTAB] += obase; 310 if (object->Dyn.info[DT_SYMTAB]) 311 object->Dyn.info[DT_SYMTAB] += obase; 312 if (object->Dyn.info[DT_RELA]) 313 object->Dyn.info[DT_RELA] += obase; 314 if (object->Dyn.info[DT_SONAME]) 315 object->Dyn.info[DT_SONAME] += object->Dyn.info[DT_STRTAB]; 316 if (object->Dyn.info[DT_RPATH]) 317 object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB]; 318 if (object->Dyn.info[DT_REL]) 319 object->Dyn.info[DT_REL] += obase; 320 if (object->Dyn.info[DT_INIT]) 321 object->Dyn.info[DT_INIT] += obase; 322 if (object->Dyn.info[DT_FINI]) 323 object->Dyn.info[DT_FINI] += obase; 324 if (object->Dyn.info[DT_JMPREL]) 325 object->Dyn.info[DT_JMPREL] += obase; 326 327 if (object->Dyn.info[DT_HASH] != 0) { 328 Elf_Word *hashtab = (Elf_Word *)object->Dyn.info[DT_HASH]; 329 330 object->nbuckets = hashtab[0]; 331 object->nchains = hashtab[1]; 332 object->buckets = hashtab + 2; 333 object->chains = object->buckets + object->nbuckets; 334 } 335 336 object->phdrp = phdrp; 337 object->phdrc = phdrc; 338 object->load_base = lbase; 339 object->obj_base = obase; 340 object->load_name = _dl_strdup(objname); 341 if (object->load_name == NULL) 342 _dl_exit(7); 343 object->load_object = _dl_loading_object; 344 if (object->load_object == object) 345 DL_DEB(("head %s\n", object->load_name)); 346 DL_DEB(("obj %s has %s as head\n", object->load_name, 347 _dl_loading_object->load_name )); 348 object->refcount = 0; 349 TAILQ_INIT(&object->child_list); 350 object->opencount = 0; /* # dlopen() & exe */ 351 object->grprefcount = 0; 352 /* default dev, inode for dlopen-able objects. */ 353 object->dev = 0; 354 object->inode = 0; 355 object->lastlookup = 0; 356 TAILQ_INIT(&object->grpsym_list); 357 TAILQ_INIT(&object->grpref_list); 358 359 if (object->dyn.rpath) { 360 object->rpath = _dl_split_path(object->dyn.rpath); 361 if ((object->obj_flags & DF_1_ORIGIN) && _dl_trust) 362 _dl_origin_subst(object); 363 } 364 365 _dl_trace_object_setup(object); 366 367 return (object); 368} 369 370static void 371_dl_tailq_free(struct dep_node *n) 372{ 373 struct dep_node *next; 374 375 while (n != NULL) { 376 next = TAILQ_NEXT(n, next_sib); 377 _dl_free(n); 378 n = next; 379 } 380} 381 382elf_object_t *free_objects; 383 384void 385_dl_cleanup_objects() 386{ 387 elf_object_t *nobj, *head; 388 struct dep_node *n, *next; 389 390 n = TAILQ_FIRST(&_dlopened_child_list); 391 while (n != NULL) { 392 next = TAILQ_NEXT(n, next_sib); 393 if (OBJECT_DLREF_CNT(n->data) == 0) { 394 TAILQ_REMOVE(&_dlopened_child_list, n, next_sib); 395 _dl_free(n); 396 } 397 n = next; 398 } 399 400 head = free_objects; 401 free_objects = NULL; 402 while (head != NULL) { 403 if (head->load_name) 404 _dl_free(head->load_name); 405 if (head->sod.sod_name) 406 _dl_free((char *)head->sod.sod_name); 407 if (head->rpath) 408 _dl_free_path(head->rpath); 409 _dl_tailq_free(TAILQ_FIRST(&head->grpsym_list)); 410 _dl_tailq_free(TAILQ_FIRST(&head->child_list)); 411 _dl_tailq_free(TAILQ_FIRST(&head->grpref_list)); 412 nobj = head->next; 413 _dl_free(head); 414 head = nobj; 415 } 416} 417 418void 419_dl_remove_object(elf_object_t *object) 420{ 421 object->prev->next = object->next; 422 if (object->next) 423 object->next->prev = object->prev; 424 425 if (_dl_last_object == object) 426 _dl_last_object = object->prev; 427 428 object->next = free_objects; 429 free_objects = object; 430} 431 432/* 433 * mprotect a segment to the indicated protection. If 'addr' is non-zero, 434 * then it's the start address, else the value of 'start_sym' is the start. 435 * The value of 'end_sym' is the end address. The start is rounded down 436 * and the end is rounded up to page boundaries. Returns 'addr' or the 437 * address of the start symbol. 438 */ 439void * 440_dl_protect_segment(elf_object_t *object, Elf_Addr addr, 441 const char *start_sym, const char *end_sym, int prot) 442{ 443 const Elf_Sym *this; 444 Elf_Addr ooff, start, end; 445 446 if (addr == 0) { 447 this = NULL; 448 ooff = _dl_find_symbol(start_sym, &this, 449 SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, 450 object, NULL); 451 /* If not found, nothing to do */ 452 if (this == NULL) 453 return (NULL); 454 addr = ooff + this->st_value; 455 } 456 457 this = NULL; 458 ooff = _dl_find_symbol(end_sym, &this, 459 SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL); 460 if (this == NULL) 461 addr = 0; 462 else { 463 end = ooff + this->st_value; 464 if (addr < end) { 465 start = ELF_TRUNC(addr, _dl_pagesz); 466 end = ELF_ROUND(end, _dl_pagesz); 467 _dl_mprotect((void *)start, end - start, prot); 468 } 469 } 470 471 return ((void *)addr); 472} 473 474 475sym_cache *_dl_symcache; 476int _dl_symcachestat_hits; 477int _dl_symcachestat_lookups; 478 479 480Elf_Addr 481_dl_find_symbol_bysym(elf_object_t *req_obj, unsigned int symidx, 482 const Elf_Sym **this, int flags, const Elf_Sym *ref_sym, const elf_object_t **pobj) 483{ 484 Elf_Addr ret; 485 const Elf_Sym *sym; 486 const char *symn; 487 const elf_object_t *sobj; 488 489 _dl_symcachestat_lookups ++; 490 if (_dl_symcache != NULL && 491 symidx < req_obj->nchains && 492 _dl_symcache[symidx].obj != NULL && 493 _dl_symcache[symidx].sym != NULL && 494 _dl_symcache[symidx].flags == flags) { 495 496 _dl_symcachestat_hits++; 497 sobj = _dl_symcache[symidx].obj; 498 *this = _dl_symcache[symidx].sym; 499 if (pobj) 500 *pobj = sobj; 501 if (_dl_prebind_validate) /* XXX */ 502 prebind_validate(req_obj, symidx, flags, ref_sym); 503 return sobj->obj_base; 504 } 505 506 sym = req_obj->dyn.symtab; 507 sym += symidx; 508 symn = req_obj->dyn.strtab + sym->st_name; 509 510 ret = _dl_find_symbol(symn, this, flags, ref_sym, req_obj, &sobj); 511 512 if (pobj) 513 *pobj = sobj; 514 515 if (_dl_symcache != NULL && symidx < req_obj->nchains) { 516#if 0 517 DL_DEB(("cache miss %d %p %p, %p %p %s %s %d %d %s\n", 518 symidx, 519 _dl_symcache[symidx].sym, *this, 520 _dl_symcache[symidx].obj, sobj, sobj->load_name, 521 sobj->dyn.strtab + (*this)->st_name, 522 _dl_symcache[symidx].flags, flags, req_obj->load_name)); 523#endif 524 525 _dl_symcache[symidx].sym = *this; 526 _dl_symcache[symidx].obj = sobj; 527 _dl_symcache[symidx].flags = flags; 528 } 529 530 return ret; 531} 532 533int _dl_searchnum = 0; 534void 535_dl_newsymsearch(void) 536{ 537 _dl_searchnum += 1; 538 539 if (_dl_searchnum < 0) { 540 /* 541 * If the signed number rolls over, reset all counters so 542 * we dont get accidental collision. 543 */ 544 elf_object_t *walkobj; 545 for (walkobj = _dl_objects; 546 walkobj != NULL; 547 walkobj = walkobj->next) { 548 walkobj->lastlookup = 0; 549 } 550 _dl_searchnum = 1; 551 } 552} 553 554static int 555_dl_find_symbol_obj(elf_object_t *object, const char *name, unsigned long hash, 556 int flags, const Elf_Sym **this, const Elf_Sym **weak_sym, 557 elf_object_t **weak_object) 558{ 559 const Elf_Sym *symt = object->dyn.symtab; 560 const char *strt = object->dyn.strtab; 561 long si; 562 const char *symn; 563 564 for (si = object->buckets[hash % object->nbuckets]; 565 si != STN_UNDEF; si = object->chains[si]) { 566 const Elf_Sym *sym = symt + si; 567 568 if (sym->st_value == 0) 569 continue; 570 571 if (ELF_ST_TYPE(sym->st_info) != STT_NOTYPE && 572 ELF_ST_TYPE(sym->st_info) != STT_OBJECT && 573 ELF_ST_TYPE(sym->st_info) != STT_FUNC) 574 continue; 575 576 symn = strt + sym->st_name; 577 if (sym != *this && _dl_strcmp(symn, name)) 578 continue; 579 580 /* allow this symbol if we are referring to a function 581 * which has a value, even if section is UNDEF. 582 * this allows &func to refer to PLT as per the 583 * ELF spec. st_value is checked above. 584 * if flags has SYM_PLT set, we must have actual 585 * symbol, so this symbol is skipped. 586 */ 587 if (sym->st_shndx == SHN_UNDEF) { 588 if ((flags & SYM_PLT) || sym->st_value == 0 || 589 ELF_ST_TYPE(sym->st_info) != STT_FUNC) 590 continue; 591 } 592 593 if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL) { 594 *this = sym; 595 return 1; 596 } else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) { 597 if (!*weak_sym) { 598 *weak_sym = sym; 599 *weak_object = object; 600 } 601 } 602 } 603 return 0; 604} 605 606Elf_Addr 607_dl_find_symbol(const char *name, const Elf_Sym **this, 608 int flags, const Elf_Sym *ref_sym, elf_object_t *req_obj, 609 const elf_object_t **pobj) 610{ 611 const Elf_Sym *weak_sym = NULL; 612 unsigned long h = 0; 613 const char *p = name; 614 elf_object_t *object = NULL, *weak_object = NULL; 615 int found = 0; 616 struct dep_node *n, *m; 617 618 619 while (*p) { 620 unsigned long g; 621 h = (h << 4) + *p++; 622 if ((g = h & 0xf0000000)) 623 h ^= g >> 24; 624 h &= ~g; 625 } 626 627 if (req_obj->dyn.symbolic) 628 if (_dl_find_symbol_obj(req_obj, name, h, flags, this, &weak_sym, 629 &weak_object)) { 630 object = req_obj; 631 found = 1; 632 goto found; 633 } 634 635 if (flags & SYM_SEARCH_OBJ) { 636 if (_dl_find_symbol_obj(req_obj, name, h, flags, this, 637 &weak_sym, &weak_object)) { 638 object = req_obj; 639 found = 1; 640 } 641 } else if (flags & SYM_DLSYM) { 642 if (_dl_find_symbol_obj(req_obj, name, h, flags, this, 643 &weak_sym, &weak_object)) { 644 object = req_obj; 645 found = 1; 646 } 647 if (weak_object != NULL && found == 0) { 648 object=weak_object; 649 *this = weak_sym; 650 found = 1; 651 } 652 /* search dlopened obj and all children */ 653 654 if (found == 0) { 655 TAILQ_FOREACH(n, &req_obj->load_object->grpsym_list, 656 next_sib) { 657 if (_dl_find_symbol_obj(n->data, name, h, 658 flags, this, 659 &weak_sym, &weak_object)) { 660 object = n->data; 661 found = 1; 662 break; 663 } 664 } 665 } 666 } else { 667 int skip = 0; 668 669 if ((flags & SYM_SEARCH_SELF) || (flags & SYM_SEARCH_NEXT)) 670 skip = 1; 671 672 _dl_newsymsearch(); 673 674 /* 675 * search dlopened objects: global or req_obj == dlopened_obj 676 * and and it's children 677 */ 678 TAILQ_FOREACH(n, &_dlopened_child_list, next_sib) { 679 if (((n->data->obj_flags & DF_1_GLOBAL) == 0) && 680 (n->data != req_obj->load_object)) 681 continue; 682 683 n->data->lastlookup_head = _dl_searchnum; 684 TAILQ_FOREACH(m, &n->data->grpsym_list, next_sib) { 685 if (skip == 1) { 686 if (m->data == req_obj) { 687 skip = 0; 688 if (flags & SYM_SEARCH_NEXT) 689 continue; 690 } else 691 continue; 692 } 693 if ((flags & SYM_SEARCH_OTHER) && 694 (m->data == req_obj)) 695 continue; 696 m->data->lastlookup = _dl_searchnum; 697 if (_dl_find_symbol_obj(m->data, name, h, flags, 698 this, &weak_sym, &weak_object)) { 699 object = m->data; 700 found = 1; 701 goto found; 702 } 703 } 704 } 705 } 706 707found: 708 if (weak_object != NULL && found == 0) { 709 object=weak_object; 710 *this = weak_sym; 711 found = 1; 712 } 713 714 715 if (found == 0) { 716 if ((ref_sym == NULL || 717 (ELF_ST_BIND(ref_sym->st_info) != STB_WEAK)) && 718 (flags & SYM_WARNNOTFOUND)) 719 _dl_printf("%s:%s: undefined symbol '%s'\n", 720 _dl_progname, req_obj->load_name, name); 721 return (0); 722 } 723 724 if (ref_sym != NULL && ref_sym->st_size != 0 && 725 (ref_sym->st_size != (*this)->st_size) && 726 (ELF_ST_TYPE((*this)->st_info) != STT_FUNC) ) { 727 _dl_printf("%s:%s: %s : WARNING: " 728 "symbol(%s) size mismatch, relink your program\n", 729 _dl_progname, req_obj->load_name, 730 object->load_name, name); 731 } 732 733 if (pobj) 734 *pobj = object; 735 736 return (object->obj_base); 737} 738 739void 740_dl_debug_state(void) 741{ 742 /* Debugger stub */ 743} 744