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