/* * Add a new dynamic object to the object list. */ void _dl_add_object(elf_object_t *object) { /* if a .so is marked nodelete, then add a reference */ if (object->obj_flags & DF_1_NODELETE && (object->status & STAT_NODELETE) == 0) { DL_DEB(("objname %s is nodelete\n", object->load_name)); object->refcount++; object->status |= STAT_NODELETE; } /* * if this is a new object, prev will be NULL * != NULL if an object already in the list * prev == NULL for the first item in the list, but that will * be the executable. */ if (object->prev != NULL) return; if (_dl_objects == NULL) { /* First object ? */ _dl_last_object = _dl_objects = object; } else { _dl_last_object->next = object; object->prev = _dl_last_object; _dl_last_object = object; } }
/* * Add a new dynamic object to the object list. */ void _dl_add_object(elf_object_t *object) { /* * If a .so is marked nodelete, then the entire load group that it's * in needs to be kept around forever, so add a reference there. * XXX It would be better if we tracked inter-object dependencies * from relocations and didn't leave dangling pointers when a load * group was partially unloaded. That would render this unnecessary. */ if (object->obj_flags & DF_1_NODELETE && (object->load_object->status & STAT_NODELETE) == 0) { DL_DEB(("objname %s is nodelete\n", object->load_name)); object->load_object->opencount++; object->load_object->status |= STAT_NODELETE; } /* * if this is a new object, prev will be NULL * != NULL if an object already in the list * prev == NULL for the first item in the list, but that will * be the executable. */ if (object->prev != NULL) return; if (_dl_objects == NULL) { /* First object ? */ _dl_last_object = _dl_objects = object; } else { _dl_last_object->next = object; object->prev = _dl_last_object; _dl_last_object = object; } }
void _dl_unload_shlib(elf_object_t *object) { struct dep_node *n; DL_DEB(("unload_shlib called on %s\n", object->load_name)); if (OBJECT_REF_CNT(object) == 0 && (object->status & STAT_UNLOADED) == 0) { object->status |= STAT_UNLOADED; TAILQ_FOREACH(n, &object->child_list, next_sib) _dl_unload_shlib(n->data); TAILQ_FOREACH(n, &object->grpref_list, next_sib) _dl_unload_shlib(n->data); DL_DEB(("unload_shlib unloading on %s\n", object->load_name)); _dl_load_list_free(object->load_list); _dl_munmap((void *)object->load_base, object->load_size); _dl_remove_object(object); } }
/* * Resolve a symbol at run-time. */ Elf_Addr _dl_bind(elf_object_t *object, int reloff) { Elf_RelA *rela; Elf_Addr *addr, ooff; const Elf_Sym *sym, *this; const char *symn; sigset_t savedmask; rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); addr = (Elf_Addr *)(object->obj_base + rela->r_offset); if (object->plt_size != 0 && !(*addr >= object->plt_start && *addr < (object->plt_start + object->plt_size ))) { /* something is broken, relocation has already occurred */ #if 0 DL_DEB(("*addr doesn't point into plt %p obj %s\n", *addr, object->load_name)); #endif return *addr; } sym = object->dyn.symtab; sym += ELF64_R_SYM(rela->r_info); symn = object->dyn.strtab + sym->st_name; this = NULL; ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, NULL); if (this == NULL) { _dl_printf("lazy binding failed!\n"); *((int *)0) = 0; /* XXX */ } /* if PLT is protected, allow the write */ if (object->plt_size != 0) { _dl_thread_bind_lock(0, &savedmask); _dl_mprotect(addr, sizeof(Elf_Addr), PROT_READ|PROT_WRITE); } *addr = ooff + this->st_value + rela->r_addend; /* if PLT is (to be protected, change back to RO/X */ if (object->plt_size != 0) { _dl_mprotect(addr, sizeof(Elf_Addr), PROT_READ); _dl_thread_bind_lock(1, &savedmask); } return *addr; }
/* * Resolve a symbol at run-time. */ uint64_t _dl_bind(elf_object_t *object, int reloff) { const elf_object_t *sobj; const Elf_Sym *sym, *this; Elf_Addr *addr, ooff; const char *symn; Elf_Addr value; Elf_RelA *rela; sigset_t savedmask; rela = (Elf_RelA *)object->dyn.jmprel + reloff; sym = object->dyn.symtab; sym += ELF_R_SYM(rela->r_info); symn = object->dyn.strtab + sym->st_name; addr = (Elf_Addr *)(object->obj_base + rela->r_offset); this = NULL; ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj); if (this == NULL) { _dl_printf("lazy binding failed!\n"); *(volatile int *)0 = 0; /* XXX */ } DL_DEB(("%s: %s\n", symn, sobj->load_name)); value = ooff + this->st_value + rela->r_addend; if (sobj->traced && _dl_trace_plt(sobj, symn)) return ((uint64_t)value << 32) | (Elf_Addr)sobj->dyn.pltgot; /* if PLT+GOT is protected, allow the write */ if (object->got_size != 0) { _dl_thread_bind_lock(0, &savedmask); /* mprotect the actual modified region, not the whole plt */ _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 2, PROT_READ|PROT_WRITE|PROT_EXEC); } addr[0] = value; addr[1] = (Elf_Addr)sobj->dyn.pltgot; /* if PLT is (to be protected, change back to RO */ if (object->got_size != 0) { /* mprotect the actual modified region, not the whole plt */ _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 3, PROT_READ|PROT_EXEC); _dl_thread_bind_lock(1, &savedmask); } return ((uint64_t)addr[0] << 32) | addr[1]; }
/* * Initialize a new dynamic object. */ elf_object_t * _dl_finalize_object(const char *objname, Elf_Dyn *dynp, Elf_Phdr *phdrp, int phdrc, const int objtype, const long lbase, const long obase) { elf_object_t *object; #if 0 _dl_printf("objname [%s], dynp %p, objtype %x lbase %lx, obase %lx\n", objname, dynp, objtype, lbase, obase); #endif object = _dl_malloc(sizeof(elf_object_t)); object->prev = object->next = NULL; object->load_dyn = dynp; while (dynp->d_tag != DT_NULL) { if (dynp->d_tag < DT_NUM) object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val; else if (dynp->d_tag >= DT_LOPROC && dynp->d_tag < DT_LOPROC + DT_PROCNUM) object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] = dynp->d_un.d_val; if (dynp->d_tag == DT_TEXTREL) object->dyn.textrel = 1; if (dynp->d_tag == DT_SYMBOLIC) object->dyn.symbolic = 1; if (dynp->d_tag == DT_BIND_NOW) object->obj_flags = RTLD_NOW; dynp++; } /* * Now relocate all pointer to dynamic info, but only * the ones which have pointer values. */ if (object->Dyn.info[DT_PLTGOT]) object->Dyn.info[DT_PLTGOT] += obase; if (object->Dyn.info[DT_HASH]) object->Dyn.info[DT_HASH] += obase; if (object->Dyn.info[DT_STRTAB]) object->Dyn.info[DT_STRTAB] += obase; if (object->Dyn.info[DT_SYMTAB]) object->Dyn.info[DT_SYMTAB] += obase; if (object->Dyn.info[DT_RELA]) object->Dyn.info[DT_RELA] += obase; if (object->Dyn.info[DT_SONAME]) object->Dyn.info[DT_SONAME] += obase; if (object->Dyn.info[DT_RPATH]) object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB]; if (object->Dyn.info[DT_REL]) object->Dyn.info[DT_REL] += obase; if (object->Dyn.info[DT_INIT]) object->Dyn.info[DT_INIT] += obase; if (object->Dyn.info[DT_FINI]) object->Dyn.info[DT_FINI] += obase; if (object->Dyn.info[DT_JMPREL]) object->Dyn.info[DT_JMPREL] += obase; if (object->Dyn.info[DT_HASH] != 0) { Elf_Word *hashtab = (Elf_Word *)object->Dyn.info[DT_HASH]; object->nbuckets = hashtab[0]; object->nchains = hashtab[1]; object->buckets = hashtab + 2; object->chains = object->buckets + object->nbuckets; } object->phdrp = phdrp; object->phdrc = phdrc; object->obj_type = objtype; object->load_base = lbase; object->obj_base = obase; object->load_name = _dl_strdup(objname); if (_dl_loading_object == NULL) { /* * no loading object, object is the loading object, * as it is either executable, or dlopened() */ _dl_loading_object = object->load_object = object; DL_DEB(("head %s\n", object->load_name )); } else { object->load_object = _dl_loading_object; } DL_DEB(("obj %s has %s as head\n", object->load_name, _dl_loading_object->load_name )); object->refcount = 0; TAILQ_INIT(&object->child_list); object->opencount = 0; /* # dlopen() & exe */ object->grprefcount = 0; /* default dev, inode for dlopen-able objects. */ object->dev = 0; object->inode = 0; TAILQ_INIT(&object->grpsym_list); TAILQ_INIT(&object->grpref_list); return(object); }
int _dl_md_reloc(elf_object_t *object, int rel, int relasz) { Elf_RelA *rela; Elf_Addr loff; int i, numrela, fails = 0; size_t size; loff = object->obj_base; numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA); rela = (Elf_RelA *)(object->Dyn.info[rel]); #ifdef DEBUG DL_DEB(("object %s relasz %x, numrela %x loff %x\n", object->load_name, object->Dyn.info[relasz], numrela, loff)); #endif if (rela == NULL) return (0); /* either it's an ld bug or a wacky hpux abi */ if (!object->dyn.pltgot) object->Dyn.info[DT_PLTGOT] += loff; if (object->dyn.init && !((Elf_Addr)object->dyn.init & 2)) { Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.init, object->dyn.pltgot); #ifdef DEBUG DL_DEB(("PLABEL32: %p:%p(_init) -> 0x%x in %s\n", object->dyn.init, object->dyn.pltgot, addr, object->load_name)); #endif object->dyn.init = (void *)addr; } if (object->dyn.fini && !((Elf_Addr)object->dyn.fini & 2)) { Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.fini, object->dyn.pltgot); #ifdef DEBUG DL_DEB(("PLABEL32: %p:%p(_fini) -> 0x%x in %s\n", object->dyn.fini, object->dyn.pltgot, addr, object->load_name)); #endif object->dyn.fini = (void *)addr; } /* * this is normally done by the crt0 code but we have to make * sure it's set here to allow constructors to call functions * that are overridden in the user binary (that are un-pic) */ if (object->obj_type == OBJTYPE_EXE) _hppa_dl_set_dp(object->dyn.pltgot); for (i = 0; i < numrela; i++, rela++) { const elf_object_t *sobj; const Elf_Sym *sym, *this; Elf_Addr *pt, ooff; const char *symn; int type; type = ELF_R_TYPE(rela->r_info); if (type == RELOC_NONE) continue; sym = object->dyn.symtab + ELF_R_SYM(rela->r_info); sobj = object; symn = object->dyn.strtab + sym->st_name; pt = (Elf_Addr *)(rela->r_offset + loff); ooff = 0; this = NULL; if (ELF_R_SYM(rela->r_info) && sym->st_name) { ooff = _dl_find_symbol_bysym(object, ELF_R_SYM(rela->r_info), &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND| ((type == RELOC_IPLT) ? SYM_PLT: SYM_NOTPLT), sym, &sobj); if (this == NULL) { if (ELF_ST_BIND(sym->st_info) != STB_WEAK) fails++; continue; } } #ifdef DEBUG DL_DEB(("*pt=%x r_addend=%x r_sym=%x\n", *pt, rela->r_addend, ELF_R_SYM(rela->r_info))); #endif switch (type) { case RELOC_DIR32: if (ELF_R_SYM(rela->r_info) && sym->st_name) { *pt = ooff + this->st_value + rela->r_addend; #ifdef DEBUG DL_DEB(("[%x]DIR32: %s:%s -> 0x%x in %s\n", i, symn, object->load_name, *pt, sobj->load_name)); #endif } else { /* * XXX should objects ever get their * sections loaded insequential this * would have to get a section number * (ELF_R_SYM(rela->r_info))-1 and then: * *pt = sect->addr + rela->r_addend; */ if (ELF_R_SYM(rela->r_info)) *pt += loff; else *pt += loff + rela->r_addend; #ifdef DEBUG DL_DEB(("[%x]DIR32: %s @ 0x%x\n", i, object->load_name, *pt)); #endif } break; case RELOC_PLABEL32: if (ELF_R_SYM(rela->r_info)) { if (ELF_ST_TYPE(this->st_info) != STT_FUNC) { DL_DEB(("[%x]PLABEL32: bad\n", i)); break; } *pt = _dl_md_plabel(sobj->obj_base + this->st_value + rela->r_addend, sobj->dyn.pltgot); #ifdef DEBUG DL_DEB(("[%x]PLABEL32: %s:%s -> 0x%x in %s\n", i, symn, object->load_name, *pt, sobj->load_name)); #endif } else { *pt = loff + rela->r_addend; #ifdef DEBUG DL_DEB(("[%x]PLABEL32: %s @ 0x%x\n", i, object->load_name, *pt)); #endif } break; case RELOC_IPLT: if (ELF_R_SYM(rela->r_info)) { pt[0] = ooff + this->st_value + rela->r_addend; pt[1] = (Elf_Addr)sobj->dyn.pltgot; #ifdef DEBUG DL_DEB(("[%x]IPLT: %s:%s -> 0x%x:0x%x in %s\n", i, symn, object->load_name, pt[0], pt[1], sobj->load_name)); #endif } else { pt[0] = loff + rela->r_addend; pt[1] = (Elf_Addr)object->dyn.pltgot; #ifdef DEBUG DL_DEB(("[%x]IPLT: %s @ 0x%x:0x%x\n", i, object->load_name, pt[0], pt[1])); #endif } break; case RELOC_COPY: { const Elf32_Sym *cpysrc = NULL; size = sym->st_size; ooff = _dl_find_symbol(symn, &cpysrc, SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT, sym, object, NULL); if (cpysrc) { _dl_bcopy((void *)(ooff + cpysrc->st_value), pt, sym->st_size); #ifdef DEBUG DL_DEB(("[%x]COPY: %s[%x]:%s -> %p[%x] in %s\n", i, symn, ooff + cpysrc->st_value, object->load_name, pt, sym->st_size, sobj->load_name)); #endif } else DL_DEB(("[%x]COPY: no sym\n", i)); break; } default: DL_DEB(("[%x]UNKNOWN(%d): type=%d off=0x%lx " "addend=0x%lx rel=0x%x\n", i, type, ELF_R_TYPE(rela->r_info), rela->r_offset, rela->r_addend, *pt)); break; } } return (fails); }
int _dl_md_reloc(elf_object_t *object, int rel, int relsz) { int i; int numrel; int fails = 0; struct load_list *load_list; Elf64_Addr loff; Elf64_Addr ooff; Elf64_Addr got_start, got_end; Elf64_Rel *relocs; const Elf64_Sym *sym, *this; loff = object->obj_base; numrel = object->Dyn.info[relsz] / sizeof(Elf64_Rel); relocs = (Elf64_Rel *)(object->Dyn.info[rel]); if (relocs == NULL) return(0); /* * Change protection of all write protected segments in the * object so we can do relocations in the .rodata section. * After relocation restore protection. */ load_list = object->load_list; while (load_list != NULL) { if ((load_list->prot & PROT_WRITE) == 0) _dl_mprotect(load_list->start, load_list->size, load_list->prot|PROT_WRITE); load_list = load_list->next; } /* XXX We need the got limits to know if reloc is in got. */ /* XXX Relocs against the got should not include the STUB address! */ this = NULL; got_start = 0; got_end = 0; ooff = _dl_find_symbol("__got_start", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) got_start = ooff + this->st_value; this = NULL; ooff = _dl_find_symbol("__got_end", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) got_end = ooff + this->st_value; DL_DEB(("relocating %d\n", numrel)); for (i = 0; i < numrel; i++, relocs++) { Elf64_Addr r_addr = relocs->r_offset + loff; const char *symn; int type; if (ELF64_R_SYM(relocs->r_info) == 0xffffff) continue; ooff = 0; sym = object->dyn.symtab; sym += ELF64_R_SYM(relocs->r_info); symn = object->dyn.strtab + sym->st_name; type = ELF64_R_TYPE(relocs->r_info); this = NULL; if (ELF64_R_SYM(relocs->r_info) && !(ELF64_ST_BIND(sym->st_info) == STB_LOCAL && ELF64_ST_TYPE (sym->st_info) == STT_NOTYPE)) { ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, sym, object, NULL); if (this == NULL) { if (ELF_ST_BIND(sym->st_info) != STB_WEAK) fails++; continue; } } switch (ELF64_R_TYPE(relocs->r_info)) { /* XXX Handle non aligned relocs. .eh_frame * XXX in libstdc++ seems to have them... */ u_int64_t robj; case R_MIPS_REL32_64: if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL && (ELF64_ST_TYPE(sym->st_info) == STT_SECTION || ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE) ) { if ((long)r_addr & 7) { _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); robj += loff + sym->st_value; _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); } else { *(u_int64_t *)r_addr += loff + sym->st_value; } } else if (this && ((long)r_addr & 7)) { _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); robj += this->st_value + ooff; _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); } else if (this) { *(u_int64_t *)r_addr += this->st_value + ooff; } break; case R_MIPS_NONE: break; default: _dl_printf("%s: unsupported relocation '%d'\n", _dl_progname, ELF64_R_TYPE(relocs->r_info)); _dl_exit(1); } } DL_DEB(("done %d fails\n", fails)); load_list = object->load_list; while (load_list != NULL) { if ((load_list->prot & PROT_WRITE) == 0) _dl_mprotect(load_list->start, load_list->size, load_list->prot); load_list = load_list->next; } return(fails); }
/* * Relocate the Global Offset Table (GOT). Currently we don't * do lazy evaluation here because the GNU linker doesn't * follow the ABI spec which says that if an external symbol * is referenced by other relocations than CALL16 and 26 it * should not be given a stub and have a zero value in the * symbol table. By not doing so, we can't use pointers to * external functions and use them in comparisons... */ int _dl_md_reloc_got(elf_object_t *object, int lazy) { int i, n; Elf64_Addr loff; Elf64_Addr ooff; Elf64_Addr *gotp; const Elf64_Sym *symp; const Elf64_Sym *this; const char *strt; if (object->status & STAT_GOT_DONE) return (0); loff = object->obj_base; strt = object->dyn.strtab; gotp = object->dyn.pltgot; n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM]; DL_DEB(("loff: '%p'\n", loff)); /* * Set up pointers for run time (lazy) resolving. */ gotp[0] = (long)_dl_bind_start; gotp[1] = (long)object; /* First do all local references. */ for (i = 2; i < n; i++) { gotp[i] += loff; } gotp += n; symp = object->dyn.symtab; symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; n = object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] - object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; this = NULL; object->plt_size = 0; object->got_size = 0; ooff = _dl_find_symbol("__got_start", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->got_start = ooff + this->st_value; this = NULL; ooff = _dl_find_symbol("__got_end", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->got_size = ooff + this->st_value - object->got_start; /* * Then do all global references according to the ABI. * Quickstart is not yet implemented. */ while (n--) { if (symp->st_shndx == SHN_UNDEF && ELF64_ST_TYPE(symp->st_info) == STT_FUNC) { if (symp->st_value == 0 || !lazy) { this = NULL; ooff = _dl_find_symbol(strt + symp->st_name, &this, SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, symp, object, NULL); if (this) *gotp = this->st_value + ooff; } else *gotp = symp->st_value + loff; } else if (symp->st_shndx == SHN_COMMON || symp->st_shndx == SHN_UNDEF) { this = NULL; ooff = _dl_find_symbol(strt + symp->st_name, &this, SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, symp, object, NULL); if (this) *gotp = this->st_value + ooff; } else if (ELF64_ST_TYPE(symp->st_info) == STT_FUNC && symp->st_value != *gotp) { *gotp += loff; } else { /* Resolve all others immediately */ this = NULL; ooff = _dl_find_symbol(strt + symp->st_name, &this, SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, symp, object, NULL); if (this) *gotp = this->st_value + ooff; else *gotp = symp->st_value + loff; } gotp++; symp++; } object->status |= STAT_GOT_DONE; DL_DEB(("got: %x, %x\n", object->got_start, object->got_size)); if (object->got_size != 0) _dl_mprotect((void*)object->got_start, object->got_size, PROT_READ); return (0); }
elf_object_t * _dl_tryload_shlib(const char *libname, int type, int flags) { int libfile, i; struct load_list *next_load, *load_list = NULL; Elf_Addr maxva = 0, minva = ELFDEFNNAME(NO_ADDR); Elf_Addr libaddr, loff, align = _dl_pagesz - 1; elf_object_t *object; char hbuf[4096]; Elf_Dyn *dynp = 0; Elf_Ehdr *ehdr; Elf_Phdr *phdp; Elf_Phdr *ptls = 0; struct stat sb; void *prebind_data; #define ROUND_PG(x) (((x) + align) & ~(align)) #define TRUNC_PG(x) ((x) & ~(align)) libfile = _dl_open(libname, O_RDONLY); if (libfile < 0) { _dl_errno = DL_CANT_OPEN; return(0); } if ( _dl_fstat(libfile, &sb) < 0) { _dl_errno = DL_CANT_OPEN; return(0); } for (object = _dl_objects; object != NULL; object = object->next) { if (object->dev == sb.st_dev && object->inode == sb.st_ino) { object->obj_flags |= flags & DF_1_GLOBAL; _dl_close(libfile); if (_dl_loading_object == NULL) _dl_loading_object = object; if (object->load_object != _dl_objects && object->load_object != _dl_loading_object) { _dl_link_grpref(object->load_object, _dl_loading_object); } return(object); } } _dl_read(libfile, hbuf, sizeof(hbuf)); ehdr = (Elf_Ehdr *)hbuf; if (ehdr->e_ident[0] != ELFMAG0 || ehdr->e_ident[1] != ELFMAG1 || ehdr->e_ident[2] != ELFMAG2 || ehdr->e_ident[3] != ELFMAG3 || ehdr->e_type != ET_DYN || ehdr->e_machine != MACHID) { _dl_close(libfile); _dl_errno = DL_NOT_ELF; return(0); } /* * Alright, we might have a winner! * Figure out how much VM space we need. */ phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++, phdp++) { switch (phdp->p_type) { case PT_LOAD: if (phdp->p_vaddr < minva) minva = phdp->p_vaddr; if (phdp->p_vaddr + phdp->p_memsz > maxva) maxva = phdp->p_vaddr + phdp->p_memsz; break; case PT_DYNAMIC: dynp = (Elf_Dyn *)phdp->p_vaddr; break; case PT_TLS: ptls = phdp; break; default: break; } } minva = TRUNC_PG(minva); maxva = ROUND_PG(maxva); /* * We map the entire area to see that we can get the VM * space required. Map it unaccessible to start with. * * We must map the file we'll map later otherwise the VM * system won't be able to align the mapping properly * on VAC architectures. */ libaddr = (Elf_Addr)_dl_mmap(0, maxva - minva, PROT_NONE, MAP_PRIVATE|MAP_FILE, libfile, 0); if (_dl_mmap_error(libaddr)) { _dl_printf("%s: rtld mmap failed mapping %s.\n", _dl_progname, libname); _dl_close(libfile); _dl_errno = DL_CANT_MMAP; return(0); } loff = libaddr - minva; phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++, phdp++) { switch (phdp->p_type) { case PT_LOAD: { char *start = (char *)(TRUNC_PG(phdp->p_vaddr)) + loff; Elf_Addr off = (phdp->p_vaddr & align); Elf_Addr size = off + phdp->p_filesz; void *res; if (size != 0) { res = _dl_mmap(start, ROUND_PG(size), PFLAGS(phdp->p_flags), MAP_FIXED|MAP_PRIVATE, libfile, TRUNC_PG(phdp->p_offset)); } else res = NULL; /* silence gcc */ next_load = _dl_malloc(sizeof(struct load_list)); next_load->next = load_list; load_list = next_load; next_load->start = start; next_load->size = size; next_load->prot = PFLAGS(phdp->p_flags); if (size != 0 && _dl_mmap_error(res)) { _dl_printf("%s: rtld mmap failed mapping %s.\n", _dl_progname, libname); _dl_close(libfile); _dl_errno = DL_CANT_MMAP; _dl_munmap((void *)libaddr, maxva - minva); _dl_load_list_free(load_list); return(0); } if (phdp->p_flags & PF_W) { /* Zero out everything past the EOF */ if ((size & align) != 0) _dl_memset(start + size, 0, _dl_pagesz - (size & align)); if (ROUND_PG(size) == ROUND_PG(off + phdp->p_memsz)) continue; start = start + ROUND_PG(size); size = ROUND_PG(off + phdp->p_memsz) - ROUND_PG(size); res = _dl_mmap(start, size, PFLAGS(phdp->p_flags), MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); if (_dl_mmap_error(res)) { _dl_printf("%s: rtld mmap failed mapping %s.\n", _dl_progname, libname); _dl_close(libfile); _dl_errno = DL_CANT_MMAP; _dl_munmap((void *)libaddr, maxva - minva); _dl_load_list_free(load_list); return(0); } } break; } case PT_OPENBSD_RANDOMIZE: _dl_randombuf((char *)(phdp->p_vaddr + loff), phdp->p_memsz); break; default: break; } } prebind_data = prebind_load_fd(libfile, libname); _dl_close(libfile); dynp = (Elf_Dyn *)((unsigned long)dynp + loff); object = _dl_finalize_object(libname, dynp, (Elf_Phdr *)((char *)libaddr + ehdr->e_phoff), ehdr->e_phnum,type, libaddr, loff); if (object) { object->prebind_data = prebind_data; object->load_size = maxva - minva; /*XXX*/ object->load_list = load_list; /* set inode, dev from stat info */ object->dev = sb.st_dev; object->inode = sb.st_ino; object->obj_flags |= flags; _dl_set_sod(object->load_name, &object->sod); if (ptls && ptls->p_memsz) { if (ptls->p_filesz > ptls->p_memsz) { _dl_printf("%s: invalid tls data in %s.\n", _dl_progname, libname); _dl_exit(9); } if (ptls->p_vaddr != 0 && ptls->p_filesz != 0) { object->tls_static_data = (void *)(ptls->p_vaddr + libaddr); } _dl_tls_dtv_generation++; object->tls_index = _dl_tls_max_index++; object->tls_fsize = ptls->p_filesz; object->tls_msize = ptls->p_memsz; object->tls_align = ptls->p_align; DL_DEB(("tls %x %x %x %x %u\n", object->tls_static_data, object->tls_fsize, object->tls_msize, object->tls_align, object->tls_index)); } } else { _dl_munmap((void *)libaddr, maxva - minva); _dl_load_list_free(load_list); } return(object); }
/* * Initialize a new dynamic object. */ elf_object_t * _dl_finalize_object(const char *objname, Elf_Dyn *dynp, Elf_Phdr *phdrp, int phdrc, const int objtype, const long lbase, const long obase) { elf_object_t *object; #if 0 _dl_printf("objname [%s], dynp %p, objtype %x lbase %lx, obase %lx\n", objname, dynp, objtype, lbase, obase); #endif object = _dl_calloc(1, sizeof(elf_object_t)); if (object == NULL) _dl_exit(7); object->prev = object->next = NULL; object->load_dyn = dynp; while (dynp->d_tag != DT_NULL) { if (dynp->d_tag < DT_NUM) object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val; else if (dynp->d_tag >= DT_LOPROC && dynp->d_tag < DT_LOPROC + DT_PROCNUM) object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] = dynp->d_un.d_val; if (dynp->d_tag == DT_TEXTREL) object->dyn.textrel = 1; if (dynp->d_tag == DT_SYMBOLIC) object->dyn.symbolic = 1; if (dynp->d_tag == DT_BIND_NOW) object->obj_flags |= DF_1_NOW; if (dynp->d_tag == DT_FLAGS_1) object->obj_flags |= dynp->d_un.d_val; if (dynp->d_tag == DT_RELACOUNT) object->relacount = dynp->d_un.d_val; if (dynp->d_tag == DT_RELCOUNT) object->relcount = dynp->d_un.d_val; dynp++; } DL_DEB((" flags %s = 0x%x\n", objname, object->obj_flags )); object->obj_type = objtype; if (_dl_loading_object == NULL) { /* * no loading object, object is the loading object, * as it is either executable, or dlopened() */ _dl_loading_object = object; } if ((object->obj_flags & DF_1_NOOPEN) != 0 && _dl_loading_object->obj_type == OBJTYPE_DLO && _dl_traceld == NULL) { _dl_free(object); _dl_errno = DL_CANT_LOAD_OBJ; return(NULL); } /* * Now relocate all pointer to dynamic info, but only * the ones which have pointer values. */ if (object->Dyn.info[DT_PLTGOT]) object->Dyn.info[DT_PLTGOT] += obase; if (object->Dyn.info[DT_HASH]) object->Dyn.info[DT_HASH] += obase; if (object->Dyn.info[DT_STRTAB]) object->Dyn.info[DT_STRTAB] += obase; if (object->Dyn.info[DT_SYMTAB]) object->Dyn.info[DT_SYMTAB] += obase; if (object->Dyn.info[DT_RELA]) object->Dyn.info[DT_RELA] += obase; if (object->Dyn.info[DT_SONAME]) object->Dyn.info[DT_SONAME] += object->Dyn.info[DT_STRTAB]; if (object->Dyn.info[DT_RPATH]) object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB]; if (object->Dyn.info[DT_REL]) object->Dyn.info[DT_REL] += obase; if (object->Dyn.info[DT_INIT]) object->Dyn.info[DT_INIT] += obase; if (object->Dyn.info[DT_FINI]) object->Dyn.info[DT_FINI] += obase; if (object->Dyn.info[DT_JMPREL]) object->Dyn.info[DT_JMPREL] += obase; if (object->Dyn.info[DT_HASH] != 0) { Elf_Word *hashtab = (Elf_Word *)object->Dyn.info[DT_HASH]; object->nbuckets = hashtab[0]; object->nchains = hashtab[1]; object->buckets = hashtab + 2; object->chains = object->buckets + object->nbuckets; } object->phdrp = phdrp; object->phdrc = phdrc; object->load_base = lbase; object->obj_base = obase; object->load_name = _dl_strdup(objname); if (object->load_name == NULL) _dl_exit(7); object->load_object = _dl_loading_object; if (object->load_object == object) DL_DEB(("head %s\n", object->load_name)); DL_DEB(("obj %s has %s as head\n", object->load_name, _dl_loading_object->load_name )); object->refcount = 0; TAILQ_INIT(&object->child_list); object->opencount = 0; /* # dlopen() & exe */ object->grprefcount = 0; /* default dev, inode for dlopen-able objects. */ object->dev = 0; object->inode = 0; object->grpsym_gen = 0; TAILQ_INIT(&object->grpsym_list); TAILQ_INIT(&object->grpref_list); if (object->dyn.rpath) { object->rpath = _dl_split_path(object->dyn.rpath); if ((object->obj_flags & DF_1_ORIGIN) && _dl_trust) _dl_origin_subst(object); } _dl_trace_object_setup(object); return (object); }
/* * Perform $ORIGIN substitutions on path */ static void _dl_origin_subst_path(elf_object_t *object, const char *origin_path, char **path) { char tmp_path[PATH_MAX]; char *new_path, *tp; const char *pp, *name, *value; static struct utsname uts; size_t value_len; int skip_brace; if (uts.sysname[0] == '\0') { if (_dl_uname(&uts) != 0) return; } tp = tmp_path; pp = *path; while (*pp != '\0' && (tp - tmp_path) < sizeof(tmp_path)) { /* copy over chars up to but not including $ */ while (*pp != '\0' && *pp != '$' && (tp - tmp_path) < sizeof(tmp_path)) *tp++ = *pp++; /* substitution sequence detected */ if (*pp == '$' && (tp - tmp_path) < sizeof(tmp_path)) { pp++; if ((skip_brace = (*pp == '{'))) pp++; /* skip over name */ name = pp; while (_dl_isalnum((unsigned char)*pp) || *pp == '_') pp++; switch (_dl_subst_name(name, pp - name)) { case SUBST_ORIGIN: value = origin_path; break; case SUBST_OSNAME: value = uts.sysname; break; case SUBST_OSREL: value = uts.release; break; case SUBST_PLATFORM: value = uts.machine; break; default: value = ""; } value_len = _dl_strlen(value); if (value_len >= sizeof(tmp_path) - (tp - tmp_path)) return; _dl_bcopy(value, tp, value_len); tp += value_len; if (skip_brace && *pp == '}') pp++; } } /* no substitution made if result exceeds sizeof(tmp_path) */ if (tp - tmp_path >= sizeof(tmp_path)) return; /* NULL terminate tmp_path */ *tp = '\0'; if (_dl_strcmp(tmp_path, *path) == 0) return; new_path = _dl_strdup(tmp_path); if (new_path == NULL) return; DL_DEB(("orig_path %s\n", *path)); DL_DEB(("new_path %s\n", new_path)); _dl_free(*path); *path = new_path; }