static void compute_unresolved_hash(struct elf_info *elf) { Elf_Sym *sym; ksym_hash_t *hash_values = elf->undef_hash.start; if (!hash_values) /* .undef.hash section is not present */ return; for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) { if (sym->st_shndx == SHN_UNDEF) { /* undefined symbol */ if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL && ELF_ST_BIND(sym->st_info) != STB_WEAK) continue; else { /* GLOBAL or WEAK undefined symbols */ *hash_values = gnu_hash((unsigned char *) (elf->strtab + sym->st_name)); /* * The hash_values array stored into the * .undef.hash section that is ordered as the * undefined symbols of the .symtab */ hash_values++; } } } }
/* * Convert an Elf_Sym into an nlist structure. This fills in only the * n_value and n_type members. */ static void elf_sym_to_nlist(struct nlist *nl, Elf_Sym *s, Elf_Shdr *shdr, int shnum) { nl->n_value = s->st_value; switch (s->st_shndx) { case SHN_UNDEF: case SHN_COMMON: nl->n_type = N_UNDF; break; case SHN_ABS: nl->n_type = ELF_ST_TYPE(s->st_info) == STT_FILE ? N_FN : N_ABS; break; default: if (s->st_shndx >= shnum) nl->n_type = N_UNDF; else { Elf_Shdr *sh = shdr + s->st_shndx; nl->n_type = sh->sh_type == SHT_PROGBITS ? (sh->sh_flags & SHF_WRITE ? N_DATA : N_TEXT) : (sh->sh_type == SHT_NOBITS ? N_BSS : N_UNDF); } break; } if (ELF_ST_BIND(s->st_info) == STB_GLOBAL || ELF_ST_BIND(s->st_info) == STB_WEAK) nl->n_type |= N_EXT; }
static void link_elf_reloc_local(linker_file_t lf) { elf_file_t ef = (elf_file_t)lf; const Elf_Rel *rellim; const Elf_Rel *rel; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *sym; Elf_Addr base; int i; Elf_Size symidx; link_elf_fix_link_set(ef); /* Perform relocations without addend if there are any: */ for (i = 0; i < ef->nreltab; i++) { rel = ef->reltab[i].rel; if (rel == NULL) panic("lost a reltab!"); rellim = rel + ef->reltab[i].nrel; base = findbase(ef, ef->reltab[i].sec); if (base == 0) panic("lost base for reltab"); for ( ; rel < rellim; rel++) { symidx = ELF_R_SYM(rel->r_info); if (symidx >= ef->ddbsymcnt) continue; sym = ef->ddbsymtab + symidx; /* Only do local relocs */ if (ELF_ST_BIND(sym->st_info) != STB_LOCAL) continue; elf_reloc_local(lf, base, rel, ELF_RELOC_REL, elf_obj_lookup); } } /* Perform relocations with addend if there are any: */ for (i = 0; i < ef->nrelatab; i++) { rela = ef->relatab[i].rela; if (rela == NULL) panic("lost a relatab!"); relalim = rela + ef->relatab[i].nrela; base = findbase(ef, ef->relatab[i].sec); if (base == 0) panic("lost base for relatab"); for ( ; rela < relalim; rela++) { symidx = ELF_R_SYM(rela->r_info); if (symidx >= ef->ddbsymcnt) continue; sym = ef->ddbsymtab + symidx; /* Only do local relocs */ if (ELF_ST_BIND(sym->st_info) != STB_LOCAL) continue; elf_reloc_local(lf, base, rela, ELF_RELOC_RELA, elf_obj_lookup); } } }
static bool wantsym(const Elf_Sym *sym, const char *strtab) { int type; int bind; type = ELF_ST_TYPE(sym->st_info); bind = ELF_ST_BIND(sym->st_info); if (type != STT_FUNC || (aflag && bind == STB_LOCAL)) #if 0 || (uflag && strchr(strtab + sym->st_name, '.') != NULL)) #endif return 0; #ifdef __arm__ /* ignore what gas calls "mapping symbols" */ { const char *c = strtab + sym->st_name; if (c[0] == '$') return 0; } #endif return 1; }
Elf32_Sym *mips_elf_find_address(struct mips_cpu *pcpu, Elf32_Addr addr) { Elf32_Shdr *shsymtab = pcpu->shsymtab; unsigned n = shsymtab->sh_size / shsymtab->sh_entsize; Elf32_Addr min = (unsigned)-1; Elf32_Sym *ret = NULL; unsigned i; if((shsymtab->sh_offset + shsymtab->sh_size > pcpu->elfsz) || (addr >= pcpu->memsz)) return NULL; for(i = 0; i < n; i++) { Elf32_Sym *sym = get_sym(pcpu->elf, pcpu->elfsz, shsymtab, i); if(!sym) return NULL; if((ELF_ST_BIND(sym->st_info) != STB_GLOBAL) || (ELF_ST_TYPE(sym->st_info) >= STT_SECTION)) continue; if(sym->st_value <= addr) { Elf32_Addr diff = addr - sym->st_value; if(diff < min) { min = diff; ret = sym; if(diff == 0) break; } } } return ret; }
Elf32_Sym *mips_elf_find_symbol(struct mips_cpu *pcpu, const char *name) { Elf32_Shdr *shsymtab = pcpu->shsymtab; unsigned n =shsymtab->sh_size / shsymtab->sh_entsize; unsigned i; if(shsymtab->sh_offset + shsymtab->sh_size > pcpu->elfsz) return NULL; for(i = 0; i < n; i++) { Elf32_Sym *sym = get_sym(pcpu->elf, pcpu->elfsz, shsymtab, i); const char *symname; if(!sym) return NULL; if(ELF_ST_BIND(sym->st_info) != STB_GLOBAL) continue; symname = get_string(pcpu->elf, pcpu->elfsz, pcpu->shsymstr, sym->st_name); if(!symname) return NULL; if(Sequal(name, symname)) return sym; } return NULL; }
int sym7(void) { if(ELF_ST_TYPE(orcSYM->st_info) != STT_FILE) return 0; if(mode & REL) if(rand() % 2) return 0; unsigned char st_info = orcSYM->st_info; Elf_Section st_shndx; if(rand() % 2) do st_info = ELF_ST_INFO(rand() & 0x0f, STT_FILE); while(ELF_ST_BIND(st_info) == STB_LOCAL); if(rand() % 4 < 3){ while((st_shndx = rand() % orcHDR->e_shnum)) if(st_shndx != SHN_ABS) break; } else while((st_shndx = getElf_Section())) if(st_shndx != SHN_ABS) break; orcSYM->st_info = st_info; orcSYM->st_shndx = st_shndx; fprintf(logfp, "(SYM[%d]->st_info = 0x%.2x,", entry, orcSYM->st_info); fprintf(logfp, " st_shndx = 0x%x)", orcSYM->st_shndx); return 1; }
bool generic_get_symbol( Elf_Xword index, std::string& name, Elf64_Addr& value, Elf_Xword& size, unsigned char& bind, unsigned char& type, Elf_Half& section_index, unsigned char& other ) const { bool ret = false; if ( index < get_symbols_num() ) { const T* pSym = reinterpret_cast<const T*>( symbol_section->get_data() + index * symbol_section->get_entry_size() ); const endianess_convertor& convertor = elf_file.get_convertor(); section* string_section = elf_file.sections[get_string_table_index()]; string_section_accessor str_reader( string_section ); const char* pStr = str_reader.get_string( convertor( pSym->st_name ) ); if ( 0 != pStr ) { name = pStr; } value = convertor( pSym->st_value ); size = convertor( pSym->st_size ); bind = ELF_ST_BIND( pSym->st_info ); type = ELF_ST_TYPE( pSym->st_info ); section_index = convertor( pSym->st_shndx ); other = pSym->st_other; ret = true; } return ret; }
/* * Symbol lookup function that can be used when the symbol index is known (ie * in relocations). It uses the symbol index instead of doing a fully fledged * hash table based lookup when such is valid. For example for local symbols. * This is not only more efficient, it's also more correct. It's not always * the case that the symbol can be found through the hash table. */ static Elf_Addr elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps) { elf_file_t ef = (elf_file_t)lf; Elf_Sym *sym; const char *symbol; Elf_Addr ret; /* Don't even try to lookup the symbol if the index is bogus. */ if (symidx >= ef->ddbsymcnt) return (0); sym = ef->ddbsymtab + symidx; /* Quick answer if there is a definition included. */ if (sym->st_shndx != SHN_UNDEF) return (sym->st_value); /* If we get here, then it is undefined and needs a lookup. */ switch (ELF_ST_BIND(sym->st_info)) { case STB_LOCAL: /* Local, but undefined? huh? */ return (0); case STB_GLOBAL: /* Relative to Data or Function name */ symbol = ef->ddbstrtab + sym->st_name; /* Force a lookup failure if the symbol name is bogus. */ if (*symbol == 0) return (0); ret = ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps)); /* * Cache global lookups during module relocation. The failure * case is particularly expensive for callers, who must scan * through the entire globals table doing strcmp(). Cache to * avoid doing such work repeatedly. * * After relocation is complete, undefined globals will be * restored to SHN_UNDEF in elf_obj_cleanup_globals_cache(), * above. */ if (ret != 0) { sym->st_shndx = SHN_FBSD_CACHED; sym->st_value = ret; } return (ret); case STB_WEAK: printf("link_elf_obj: Weak symbols not supported\n"); return (0); default: return (0); } }
/* * kobj_sym_lookup: * * Symbol lookup function to be used when the symbol index * is known (ie during relocation). */ uintptr_t kobj_sym_lookup(kobj_t ko, uintptr_t symidx) { const Elf_Sym *sym; const char *symbol; int error; u_long addr; /* Don't even try to lookup the symbol if the index is bogus. */ if (symidx >= ko->ko_symcnt) return 0; sym = ko->ko_symtab + symidx; /* Quick answer if there is a definition included. */ if (sym->st_shndx != SHN_UNDEF) { return sym->st_value; } /* If we get here, then it is undefined and needs a lookup. */ switch (ELF_ST_BIND(sym->st_info)) { case STB_LOCAL: /* Local, but undefined? huh? */ kobj_error("local symbol undefined"); return 0; case STB_GLOBAL: /* Relative to Data or Function name */ symbol = ko->ko_strtab + sym->st_name; /* Force a lookup failure if the symbol name is bogus. */ if (*symbol == 0) { kobj_error("bad symbol name"); return 0; } /* * Don't need to lock, as it is known that the symbol * tables aren't going to change (we hold module_lock). */ error = ksyms_getval(NULL, symbol, &addr, KSYMS_ANY); if (error != 0) { kobj_error("symbol `%s' not found", symbol); return (uintptr_t)0; } return (uintptr_t)addr; case STB_WEAK: kobj_error("weak symbols not supported\n"); return 0; default: return 0; } }
static void parse_elf_symbols(uintptr_t mem, size_t size, Phdr_t *load, struct vdso_symtable *t, uintptr_t dynsymbol_names, Hash_t *hash, Dyn_t *dyn_symtab) { const char *vdso_symbols[VDSO_SYMBOL_MAX] = { ARCH_VDSO_SYMBOLS }; const size_t vdso_symbol_length = sizeof(t->symbols[0].name); Hash_t nbucket, nchain; Hash_t *bucket, *chain; unsigned int i, j, k; uintptr_t addr; nbucket = hash[0]; nchain = hash[1]; bucket = &hash[2]; chain = &hash[nbucket + 2]; pr_debug("nbucket %lx nchain %lx bucket %lx chain %lx\n", (long)nbucket, (long)nchain, (unsigned long)bucket, (unsigned long)chain); for (i = 0; i < VDSO_SYMBOL_MAX; i++) { const char * symbol = vdso_symbols[i]; k = elf_hash((const unsigned char *)symbol); for (j = bucket[k % nbucket]; j < nchain && chain[j] != STN_UNDEF; j = chain[j]) { addr = mem + dyn_symtab->d_un.d_ptr - load->p_vaddr; Sym_t *sym; char *name; addr += sizeof(Sym_t)*j; if (__ptr_struct_oob(addr, sizeof(Sym_t), mem, size)) continue; sym = (void *)addr; if (ELF_ST_TYPE(sym->st_info) != STT_FUNC && ELF_ST_BIND(sym->st_info) != STB_GLOBAL) continue; addr = dynsymbol_names + sym->st_name; if (__ptr_struct_oob(addr, vdso_symbol_length, mem, size)) continue; name = (void *)addr; if (std_strncmp(name, symbol, vdso_symbol_length)) continue; memcpy(t->symbols[i].name, name, vdso_symbol_length); t->symbols[i].offset = (unsigned long)sym->st_value - load->p_vaddr; break; } } }
static int simplify_symbols(struct secthdr *sechdrs, unsigned int symindex, const char *strtab, unsigned int versindex, unsigned int pcpuindex, struct module *mod) { struct symtab_s *sym = (void *)sechdrs[symindex].sh_addr; unsigned long secbase; unsigned int i, n = sechdrs[symindex].sh_size / sizeof(struct symtab_s); int ret = 0; const struct kernel_symbol *ksym; for (i = 1; i < n; i++) { switch (sym[i].st_shndx) { case SHN_COMMON: /* We compiled with -fno-common. These are not supposed to happen. */ kprintf("simplify_symbols: Common symbol: %s\n", strtab + sym[i].st_name); kprintf("%s: please compile with -fno-common\n", mod->name); ret = -1; break; case SHN_ABS: /* Don't need to do anything */ kprintf("simplify_symbols: Absolute symbol: 0x%08lx\n", (long)sym[i].st_value); break; case SHN_UNDEF: ksym = resolve_symbol(sechdrs, versindex, strtab + sym[i].st_name, mod); /* Ok if resolved. */ if (ksym) { sym[i].st_value = ksym->value; break; } /* Ok if weak. */ if (ELF_ST_BIND(sym[i].st_info) == STB_WEAK) break; kprintf("simplify_symbols: Unknown symbol %s\n", strtab + sym[i].st_name); ret = -1; break; default: if (sym[i].st_shndx == pcpuindex) secbase = (unsigned long)mod->percpu; else secbase = sechdrs[sym[i].st_shndx].sh_addr; sym[i].st_value += secbase; break; } } return ret; }
static bool wantsym(const Elf_Sym *sym, const char *strtab) { int type; int bind; type = ELF_ST_TYPE(sym->st_info); bind = ELF_ST_BIND(sym->st_info); if (type != STT_FUNC || (aflag && bind == STB_LOCAL) || (uflag && strchr(strtab + sym->st_name, '.') != NULL)) return 0; return 1; }
static Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi, const char *name) { Elf_Sym *syms; Elf_Shdr *sechdrs; Elf_Ehdr *ehdr; int i, k; const char *strtab; if (!pi->sechdrs || !pi->ehdr) return NULL; sechdrs = pi->sechdrs; ehdr = pi->ehdr; for (i = 0; i < ehdr->e_shnum; i++) { if (sechdrs[i].sh_type != SHT_SYMTAB) continue; if (sechdrs[i].sh_link >= ehdr->e_shnum) /* Invalid strtab section number */ continue; strtab = (char *)sechdrs[sechdrs[i].sh_link].sh_offset; syms = (Elf_Sym *)sechdrs[i].sh_offset; /* Go through symbols for a match */ for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) { if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL) continue; if (strcmp(strtab + syms[k].st_name, name) != 0) continue; if (syms[k].st_shndx == SHN_UNDEF || syms[k].st_shndx >= ehdr->e_shnum) { pr_debug("Symbol: %s has bad section index %d.\n", name, syms[k].st_shndx); return NULL; } /* Found the symbol we are looking for */ return &syms[k]; } } return NULL; }
void* dlsym(void* handle, const char* symbol) { ScopedPthreadMutexLocker locker(&g_dl_mutex); #if !defined(__LP64__) if (handle == NULL) { __bionic_format_dlerror("dlsym library handle is null", NULL); return NULL; } #endif if (symbol == NULL) { __bionic_format_dlerror("dlsym symbol name is null", NULL); return NULL; } soinfo* found = NULL; ElfW(Sym)* sym = NULL; if (handle == RTLD_DEFAULT) { sym = dlsym_linear_lookup(symbol, &found, NULL); } else if (handle == RTLD_NEXT) { void* caller_addr = __builtin_return_address(0); soinfo* si = find_containing_library(caller_addr); sym = NULL; if (si && si->next) { sym = dlsym_linear_lookup(symbol, &found, si->next); } } else { found = reinterpret_cast<soinfo*>(handle); sym = dlsym_handle_lookup(found, symbol); } if (sym != NULL) { unsigned bind = ELF_ST_BIND(sym->st_info); if ((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) { return reinterpret_cast<void*>(sym->st_value + found->load_bias); } __bionic_format_dlerror("symbol found but not global", symbol); return NULL; } else { __bionic_format_dlerror("undefined symbol", symbol); return NULL; } }
/* * kobj_sym_lookup: * * Symbol lookup function to be used when the symbol index * is known (ie during relocation). */ uintptr_t kobj_sym_lookup(kobj_t ko, uintptr_t symidx) { const Elf_Sym *sym; const char *symbol; /* Don't even try to lookup the symbol if the index is bogus. */ if (symidx >= ko->ko_symcnt) return 0; sym = ko->ko_symtab + symidx; /* Quick answer if there is a definition included. */ if (sym->st_shndx != SHN_UNDEF) { return (uintptr_t)sym->st_value; } /* If we get here, then it is undefined and needs a lookup. */ switch (ELF_ST_BIND(sym->st_info)) { case STB_LOCAL: /* Local, but undefined? huh? */ kobj_error(__func__, __LINE__, ko, "local symbol undefined"); return 0; case STB_GLOBAL: /* Relative to Data or Function name */ symbol = ko->ko_strtab + sym->st_name; /* Force a lookup failure if the symbol name is bogus. */ if (*symbol == 0) { kobj_error(__func__, __LINE__, ko, "bad symbol name"); return 0; } return (uintptr_t)sym->st_value; case STB_WEAK: kobj_error(__func__, __LINE__, ko, "weak symbols not supported"); return 0; default: return 0; } }
/* * kobj_checkdup: * * Scan symbol table for duplicates. */ static int kobj_checkdup(kobj_t ko) { unsigned long rval; Elf_Sym *sym, *ms; const char *name; bool dup; dup = false; for (ms = (sym = ko->ko_symtab) + ko->ko_symcnt; sym < ms; sym++) { /* Check validity of the symbol. */ if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL || sym->st_name == 0) continue; /* Check if the symbol already exists */ name = ko->ko_strtab + sym->st_name; if (ksyms_getval(NULL, name, &rval, KSYMS_EXTERN) != 0) { continue; } /* Check (and complain) about differing values */ if (sym->st_value == rval || sym->st_shndx == SHN_UNDEF) { continue; } if (strcmp(name, "_bss_start") == 0 || strcmp(name, "__bss_start") == 0 || strcmp(name, "_bss_end__") == 0 || strcmp(name, "__bss_end__") == 0 || strcmp(name, "_edata") == 0 || strcmp(name, "_end") == 0 || strcmp(name, "__end") == 0 || strcmp(name, "__end__") == 0 || strncmp(name, "__start_link_set_", 17) == 0 || strncmp(name, "__stop_link_set_", 16)) { continue; } kobj_error("global symbol `%s' redefined\n", name); dup = true; } return dup ? EEXIST : 0; }
/* * Symbol lookup function that can be used when the symbol index is known (ie * in relocations). It uses the symbol index instead of doing a fully fledged * hash table based lookup when such is valid. For example for local symbols. * This is not only more efficient, it's also more correct. It's not always * the case that the symbol can be found through the hash table. */ static int elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *result) { elf_file_t ef = lf->priv; const Elf_Sym *sym; const char *symbol; /* Don't even try to lookup the symbol if the index is bogus. */ if (symidx >= ef->ddbsymcnt) return (ENOENT); sym = ef->ddbsymtab + symidx; /* Quick answer if there is a definition included. */ if (sym->st_shndx != SHN_UNDEF) { *result = sym->st_value; return (0); } /* If we get here, then it is undefined and needs a lookup. */ switch (ELF_ST_BIND(sym->st_info)) { case STB_LOCAL: /* Local, but undefined? huh? */ return (ENOENT); case STB_GLOBAL: /* Relative to Data or Function name */ symbol = ef->ddbstrtab + sym->st_name; /* Force a lookup failure if the symbol name is bogus. */ if (*symbol == 0) return (ENOENT); return (linker_file_lookup_symbol(lf, symbol, deps, (caddr_t *)result)); case STB_WEAK: kprintf("link_elf_obj_obj: Weak symbols not supported\n"); return (ENOENT); default: return (ENOENT); } }
/** * @brief dump the absolute symbols to the header file * * @param fd file descriptor of file from which to read * @param fp file pointer to which to write * @param symTblOffset symbol table offset * @param symTblSize size of the symbol table * @param pStringTable ptr to the string table * @returns N/A */ static void headerAbsoluteSymbolsDump(int fd, FILE *fp, Elf32_Off symTblOffset, Elf32_Word symTblSize, char *pStringTable) { Elf32_Sym aSym; /* absolute symbol */ unsigned ix; /* loop counter */ unsigned numSyms; /* number of symbols in the symbol table */ size_t nBytes; /* context the symbol table: pick out absolute syms */ numSyms = symTblSize / sizeof(Elf32_Sym); lseek(fd, symTblOffset, SEEK_SET); for (ix = 0; ix < numSyms; ++ix) { /* read in a single symbol structure */ nBytes = read(fd, &aSym, sizeof(Elf32_Sym)); if (nBytes) { swabElfSym(&aSym); /* swap bytes (if required) */ } /* * Only generate definitions for global absolute symbols * of the form *_OFFSET */ if ((aSym.st_shndx == SHN_ABS) && (ELF_ST_BIND(aSym.st_info) == STB_GLOBAL)) { if ((strstr(&pStringTable[aSym.st_name], STRUCT_OFF_SUFFIX) != NULL) || (strstr(&pStringTable[aSym.st_name], STRUCT_SIZ_SUFFIX) != NULL)) { fprintf(fp, "#define\t%s\t0x%X\n", &pStringTable[aSym.st_name], aSym.st_value); } } } }
int sym6(void) { if(ELF_ST_TYPE(orcSYM->st_info) != STT_SECTION) return 0; if(mode & REL) if(rand() % 2) return 0; unsigned char st_info; do st_info = ELF_ST_INFO(rand() & 0x0f, STT_SECTION); while(ELF_ST_BIND(st_info) == STB_LOCAL); orcSYM->st_info = st_info; fprintf(logfp, "(SYM[%d]->st_info = 0x%.2x)", entry, orcSYM->st_info); return 1; }
/* * Symbol lookup function that can be used when the symbol index is known (ie * in relocations). It uses the symbol index instead of doing a fully fledged * hash table based lookup when such is valid. For example for local symbols. * This is not only more efficient, it's also more correct. It's not always * the case that the symbol can be found through the hash table. */ static int elf_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *result) { elf_file_t ef = lf->priv; const Elf_Sym *sym; const char *symbol; /* Don't even try to lookup the symbol if the index is bogus. */ if (symidx >= ef->nchains) return (ENOENT); sym = ef->symtab + symidx; /* * Don't do a full lookup when the symbol is local. It may even * fail because it may not be found through the hash table. */ if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) { /* Force lookup failure when we have an insanity. */ if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0) return (ENOENT); return ((Elf_Addr) ef->address + sym->st_value); } /* * XXX we can avoid doing a hash table based lookup for global * symbols as well. This however is not always valid, so we'll * just do it the hard way for now. Performance tweaks can * always be added. */ symbol = ef->strtab + sym->st_name; /* Force a lookup failure if the symbol name is bogus. */ if (*symbol == 0) return (ENOENT); return (linker_file_lookup_symbol(lf, symbol, deps, (caddr_t *)result)); }
/*ARGSUSED*/ static void ksyms_walk_one(void *arg, void *base, size_t size) { ksyms_walkinfo_t *kwp = arg; Shdr *symhdr = base; Shdr *strhdr = symhdr + symhdr->sh_link; size_t symsize = symhdr->sh_entsize; size_t nsyms = symhdr->sh_size / symsize; char *strings = (char *)strhdr->sh_addr; int i; for (i = 1; i < nsyms; i++) { Sym *sym = (Sym *)(symhdr->sh_addr + i * symsize); Sym tmp = *sym; char *name = strings + sym->st_name; tmp.st_name = kwp->kw_size[KW_STRINGS]; tmp.st_shndx = SHN_ABS; ksyms_emit(kwp, &tmp, sizeof (Sym), ELF_ST_BIND(sym->st_info) == STB_LOCAL ? KW_LOCALS : KW_GLOBALS); ksyms_emit(kwp, name, strlen(name) + 1, KW_STRINGS); } }
/* * Warning message for bad move target. */ void elf_move_bad(Lm_list *lml, Rt_map *lmp, Sym *sym, ulong_t num, Addr addr) { const char *name; int trace; trace = (lml->lm_flags & LML_FLG_TRC_ENABLE) && (((rtld_flags & RT_FL_SILENCERR) == 0) || (lml->lm_flags & (LML_FLG_TRC_VERBOSE | LML_FLG_TRC_WARN))); if ((trace == 0) && (DBG_ENABLED == 0)) return; if (ELF_ST_BIND(sym->st_info) != STB_LOCAL) name = (const char *)(STRTAB(lmp) + sym->st_name); else name = MSG_INTL(MSG_STR_UNKNOWN); if (trace) (void) printf(MSG_INTL(MSG_LDD_MOVE_ERR), EC_XWORD(num), name, EC_ADDR(addr)); else DBG_CALL(Dbg_move_bad(lml, num, name, addr)); }
int _dl_md_reloc(elf_object_t *object, int rel, int relasz) { long i; long numrela; int fails = 0; Elf64_Addr loff; Elf64_Rela *relas; struct load_list *llist; loff = object->obj_base; numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela); relas = (Elf64_Rela *)(object->Dyn.info[rel]); if (relas == NULL) return(0); /* * unprotect some segments if we need it. * XXX - we unprotect way to much. only the text can have cow * relocations. */ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) { for (llist = object->load_list; llist != NULL; llist = llist->next) { if (!(llist->prot & PROT_WRITE)) { _dl_mprotect(llist->start, llist->size, llist->prot|PROT_WRITE); } } } for (i = 0; i < numrela; i++, relas++) { Elf64_Addr *r_addr; Elf64_Addr ooff; const Elf64_Sym *sym, *this; const char *symn; r_addr = (Elf64_Addr *)(relas->r_offset + loff); if (ELF64_R_SYM(relas->r_info) == 0xffffffff) continue; sym = object->dyn.symtab; sym += ELF64_R_SYM(relas->r_info); symn = object->dyn.strtab + sym->st_name; this = NULL; switch (ELF64_R_TYPE(relas->r_info)) { case R_TYPE(REFQUAD): ooff = _dl_find_symbol_bysym(object, ELF64_R_SYM(relas->r_info), &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, sym, NULL); if (this == NULL) goto resolve_failed; *r_addr += ooff + this->st_value + relas->r_addend; break; case R_TYPE(RELATIVE): /* * There is a lot of unaligned RELATIVE * relocs generated by gcc in the exception handlers. */ if ((((Elf_Addr) r_addr) & 0x7) != 0) { Elf_Addr tmp; #if 0 _dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr, ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff); #endif _dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr)); tmp += loff; _dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr)); } else *r_addr += loff; break; case R_TYPE(JMP_SLOT): ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, NULL); if (this == NULL) goto resolve_failed; *r_addr = ooff + this->st_value + relas->r_addend; break; case R_TYPE(GLOB_DAT): ooff = _dl_find_symbol_bysym(object, ELF64_R_SYM(relas->r_info), &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, sym, NULL); if (this == NULL) goto resolve_failed; *r_addr = ooff + this->st_value + relas->r_addend; break; case R_TYPE(NONE): break; default: _dl_printf("%s:" " %s: unsupported relocation '%s' %d at %lx\n", _dl_progname, object->load_name, symn, ELF64_R_TYPE(relas->r_info), r_addr ); _dl_exit(1); } continue; resolve_failed: if (ELF_ST_BIND(sym->st_info) != STB_WEAK) fails++; } __asm __volatile("imb" : : : "memory"); /* reprotect the unprotected segments */ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) { for (llist = object->load_list; llist != NULL; llist = llist->next) { if (!(llist->prot & PROT_WRITE)) _dl_mprotect(llist->start, llist->size, llist->prot); } } return (fails); }
void _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) { const Elf_Rel *rel = 0, *rellim; Elf_Addr relsz = 0; const Elf_Sym *symtab = NULL, *sym; Elf_Addr *where; Elf_Addr *got = NULL; Elf_Word local_gotno = 0, symtabno = 0, gotsym = 0; size_t i; for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_REL: rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); break; case DT_RELSZ: relsz = dynp->d_un.d_val; break; case DT_SYMTAB: symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr); break; case DT_PLTGOT: got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr); break; case DT_MIPS_LOCAL_GOTNO: local_gotno = dynp->d_un.d_val; break; case DT_MIPS_SYMTABNO: symtabno = dynp->d_un.d_val; break; case DT_MIPS_GOTSYM: gotsym = dynp->d_un.d_val; break; } } i = (got[1] & GOT1_MASK) ? 2 : 1; /* Relocate the local GOT entries */ got += i; for (; i < local_gotno; i++) { *got++ += relocbase; } sym = symtab + gotsym; /* Now do the global GOT entries */ for (i = gotsym; i < symtabno; i++) { *got = sym->st_value + relocbase; ++sym; ++got; } rellim = (const Elf_Rel *)((caddr_t)rel + relsz); for (; rel < rellim; rel++) { Elf_Word r_symndx, r_type; where = (void *)(relocbase + rel->r_offset); r_symndx = ELF_R_SYM(rel->r_info); r_type = ELF_R_TYPE(rel->r_info); switch (r_type & 0xff) { case R_TYPE(REL32): { const size_t rlen = ELF_R_NXTTYPE_64_P(r_type) ? sizeof(Elf_Sxword) : sizeof(Elf_Sword); Elf_Sxword old = load_ptr(where, rlen); Elf_Sxword val = old; #ifdef __mips_n64 assert(r_type == R_TYPE(REL32) || r_type == (R_TYPE(REL32)|(R_TYPE(64) << 8))); #endif assert(r_symndx < gotsym); sym = symtab + r_symndx; assert(ELF_ST_BIND(sym->st_info) == STB_LOCAL); val += relocbase; store_ptr(where, val, sizeof(Elf_Sword)); dbg("REL32/L(%p) %p -> %p in <self>", where, (void *)old, (void *)val); store_ptr(where, val, rlen); break; } case R_TYPE(GPREL32): case R_TYPE(NONE): break; default: abort(); break; } } }
/* * Read and process the relocations for one link object, we assume all * relocation sections for loadable segments are stored contiguously in * the file. */ int elf_reloc(Rt_map *lmp, uint_t plt, int *in_nfavl, APlist **textrel) { ulong_t relbgn, relend, relsiz, basebgn, pltbgn, pltend; ulong_t _pltbgn, _pltend; ulong_t dsymndx, roffset, rsymndx, psymndx = 0; uchar_t rtype; long value, pvalue; Sym *symref, *psymref, *symdef, *psymdef; Syminfo *sip; char *name, *pname; Rt_map *_lmp, *plmp; int ret = 1, noplt = 0; int relacount = RELACOUNT(lmp), plthint = 0; Rel *rel; uint_t binfo, pbinfo; APlist *bound = NULL; /* * Although only necessary for lazy binding, initialize the first * global offset entry to go to elf_rtbndr(). dbx(1) seems * to find this useful. */ if ((plt == 0) && PLTGOT(lmp)) { mmapobj_result_t *mpp; /* * Make sure the segment is writable. */ if ((((mpp = find_segment((caddr_t)PLTGOT(lmp), lmp)) != NULL) && ((mpp->mr_prot & PROT_WRITE) == 0)) && ((set_prot(lmp, mpp, 1) == 0) || (aplist_append(textrel, mpp, AL_CNT_TEXTREL) == NULL))) return (0); elf_plt_init(PLTGOT(lmp), (caddr_t)lmp); } /* * Initialize the plt start and end addresses. */ if ((pltbgn = (ulong_t)JMPREL(lmp)) != 0) pltend = pltbgn + (ulong_t)(PLTRELSZ(lmp)); relsiz = (ulong_t)(RELENT(lmp)); basebgn = ADDR(lmp); if (PLTRELSZ(lmp)) plthint = PLTRELSZ(lmp) / relsiz; /* * If we've been called upon to promote an RTLD_LAZY object to an * RTLD_NOW then we're only interested in scaning the .plt table. * An uninitialized .plt is the case where the associated got entry * points back to the plt itself. Determine the range of the real .plt * entries using the _PROCEDURE_LINKAGE_TABLE_ symbol. */ if (plt) { Slookup sl; Sresult sr; relbgn = pltbgn; relend = pltend; if (!relbgn || (relbgn == relend)) return (1); /* * Initialize the symbol lookup, and symbol result, data * structures. */ SLOOKUP_INIT(sl, MSG_ORIG(MSG_SYM_PLT), lmp, lmp, ld_entry_cnt, elf_hash(MSG_ORIG(MSG_SYM_PLT)), 0, 0, 0, LKUP_DEFT); SRESULT_INIT(sr, MSG_ORIG(MSG_SYM_PLT)); if (elf_find_sym(&sl, &sr, &binfo, NULL) == 0) return (1); symdef = sr.sr_sym; _pltbgn = symdef->st_value; if (!(FLAGS(lmp) & FLG_RT_FIXED) && (symdef->st_shndx != SHN_ABS)) _pltbgn += basebgn; _pltend = _pltbgn + (((PLTRELSZ(lmp) / relsiz)) * M_PLT_ENTSIZE) + M_PLT_RESERVSZ; } else { /* * The relocation sections appear to the run-time linker as a * single table. Determine the address of the beginning and end * of this table. There are two different interpretations of * the ABI at this point: * * o The REL table and its associated RELSZ indicate the * concatenation of *all* relocation sections (this is the * model our link-editor constructs). * * o The REL table and its associated RELSZ indicate the * concatenation of all *but* the .plt relocations. These * relocations are specified individually by the JMPREL and * PLTRELSZ entries. * * Determine from our knowledege of the relocation range and * .plt range, the range of the total relocation table. Note * that one other ABI assumption seems to be that the .plt * relocations always follow any other relocations, the * following range checking drops that assumption. */ relbgn = (ulong_t)(REL(lmp)); relend = relbgn + (ulong_t)(RELSZ(lmp)); if (pltbgn) { if (!relbgn || (relbgn > pltbgn)) relbgn = pltbgn; if (!relbgn || (relend < pltend)) relend = pltend; } } if (!relbgn || (relbgn == relend)) { DBG_CALL(Dbg_reloc_run(lmp, 0, plt, DBG_REL_NONE)); return (1); } DBG_CALL(Dbg_reloc_run(lmp, M_REL_SHT_TYPE, plt, DBG_REL_START)); /* * If we're processing a dynamic executable in lazy mode there is no * need to scan the .rel.plt table, however if we're processing a shared * object in lazy mode the .got addresses associated to each .plt must * be relocated to reflect the location of the shared object. */ if (pltbgn && ((MODE(lmp) & RTLD_NOW) == 0) && (FLAGS(lmp) & FLG_RT_FIXED)) noplt = 1; sip = SYMINFO(lmp); /* * Loop through relocations. */ while (relbgn < relend) { mmapobj_result_t *mpp; uint_t sb_flags = 0; rtype = ELF_R_TYPE(((Rel *)relbgn)->r_info, M_MACH); /* * If this is a RELATIVE relocation in a shared object (the * common case), and if we are not debugging, then jump into a * tighter relocation loop (elf_reloc_relative). */ if ((rtype == R_386_RELATIVE) && ((FLAGS(lmp) & FLG_RT_FIXED) == 0) && (DBG_ENABLED == 0)) { if (relacount) { relbgn = elf_reloc_relative_count(relbgn, relacount, relsiz, basebgn, lmp, textrel, 0); relacount = 0; } else { relbgn = elf_reloc_relative(relbgn, relend, relsiz, basebgn, lmp, textrel, 0); } if (relbgn >= relend) break; rtype = ELF_R_TYPE(((Rel *)relbgn)->r_info, M_MACH); } roffset = ((Rel *)relbgn)->r_offset; /* * If this is a shared object, add the base address to offset. */ if (!(FLAGS(lmp) & FLG_RT_FIXED)) { /* * If we're processing lazy bindings, we have to step * through the plt entries and add the base address * to the corresponding got entry. */ if (plthint && (plt == 0) && (rtype == R_386_JMP_SLOT) && ((MODE(lmp) & RTLD_NOW) == 0)) { relbgn = elf_reloc_relative_count(relbgn, plthint, relsiz, basebgn, lmp, textrel, 0); plthint = 0; continue; } roffset += basebgn; } rsymndx = ELF_R_SYM(((Rel *)relbgn)->r_info); rel = (Rel *)relbgn; relbgn += relsiz; /* * Optimizations. */ if (rtype == R_386_NONE) continue; if (noplt && ((ulong_t)rel >= pltbgn) && ((ulong_t)rel < pltend)) { relbgn = pltend; continue; } /* * If we're promoting plts, determine if this one has already * been written. */ if (plt && ((*(ulong_t *)roffset < _pltbgn) || (*(ulong_t *)roffset > _pltend))) continue; /* * If this relocation is not against part of the image * mapped into memory we skip it. */ if ((mpp = find_segment((caddr_t)roffset, lmp)) == NULL) { elf_reloc_bad(lmp, (void *)rel, rtype, roffset, rsymndx); continue; } binfo = 0; /* * If a symbol index is specified then get the symbol table * entry, locate the symbol definition, and determine its * address. */ if (rsymndx) { /* * If a Syminfo section is provided, determine if this * symbol is deferred, and if so, skip this relocation. */ if (sip && is_sym_deferred((ulong_t)rel, basebgn, lmp, textrel, sip, rsymndx)) continue; /* * Get the local symbol table entry. */ symref = (Sym *)((ulong_t)SYMTAB(lmp) + (rsymndx * SYMENT(lmp))); /* * If this is a local symbol, just use the base address. * (we should have no local relocations in the * executable). */ if (ELF_ST_BIND(symref->st_info) == STB_LOCAL) { value = basebgn; name = NULL; /* * Special case TLS relocations. */ if (rtype == R_386_TLS_DTPMOD32) { /* * Use the TLS modid. */ value = TLSMODID(lmp); } else if (rtype == R_386_TLS_TPOFF) { if ((value = elf_static_tls(lmp, symref, rel, rtype, 0, roffset, 0)) == 0) { ret = 0; break; } } } else { /* * If the symbol index is equal to the previous * symbol index relocation we processed then * reuse the previous values. (Note that there * have been cases where a relocation exists * against a copy relocation symbol, our ld(1) * should optimize this away, but make sure we * don't use the same symbol information should * this case exist). */ if ((rsymndx == psymndx) && (rtype != R_386_COPY)) { /* LINTED */ if (psymdef == 0) { DBG_CALL(Dbg_bind_weak(lmp, (Addr)roffset, (Addr) (roffset - basebgn), name)); continue; } /* LINTED */ value = pvalue; /* LINTED */ name = pname; /* LINTED */ symdef = psymdef; /* LINTED */ symref = psymref; /* LINTED */ _lmp = plmp; /* LINTED */ binfo = pbinfo; if ((LIST(_lmp)->lm_tflags | AFLAGS(_lmp)) & LML_TFLG_AUD_SYMBIND) { value = audit_symbind(lmp, _lmp, /* LINTED */ symdef, dsymndx, value, &sb_flags); } } else { Slookup sl; Sresult sr; /* * Lookup the symbol definition. * Initialize the symbol lookup, and * symbol result, data structures. */ name = (char *)(STRTAB(lmp) + symref->st_name); SLOOKUP_INIT(sl, name, lmp, 0, ld_entry_cnt, 0, rsymndx, symref, rtype, LKUP_STDRELOC); SRESULT_INIT(sr, name); symdef = NULL; if (lookup_sym(&sl, &sr, &binfo, in_nfavl)) { name = (char *)sr.sr_name; _lmp = sr.sr_dmap; symdef = sr.sr_sym; } /* * If the symbol is not found and the * reference was not to a weak symbol, * report an error. Weak references * may be unresolved. */ /* BEGIN CSTYLED */ if (symdef == 0) { if (sl.sl_bind != STB_WEAK) { if (elf_reloc_error(lmp, name, rel, binfo)) continue; ret = 0; break; } else { psymndx = rsymndx; psymdef = 0; DBG_CALL(Dbg_bind_weak(lmp, (Addr)roffset, (Addr) (roffset - basebgn), name)); continue; } } /* END CSTYLED */ /* * If symbol was found in an object * other than the referencing object * then record the binding. */ if ((lmp != _lmp) && ((FLAGS1(_lmp) & FL1_RT_NOINIFIN) == 0)) { if (aplist_test(&bound, _lmp, AL_CNT_RELBIND) == 0) { ret = 0; break; } } /* * Calculate the location of definition; * symbol value plus base address of * containing shared object. */ if (IS_SIZE(rtype)) value = symdef->st_size; else value = symdef->st_value; if (!(FLAGS(_lmp) & FLG_RT_FIXED) && !(IS_SIZE(rtype)) && (symdef->st_shndx != SHN_ABS) && (ELF_ST_TYPE(symdef->st_info) != STT_TLS)) value += ADDR(_lmp); /* * Retain this symbol index and the * value in case it can be used for the * subsequent relocations. */ if (rtype != R_386_COPY) { psymndx = rsymndx; pvalue = value; pname = name; psymdef = symdef; psymref = symref; plmp = _lmp; pbinfo = binfo; } if ((LIST(_lmp)->lm_tflags | AFLAGS(_lmp)) & LML_TFLG_AUD_SYMBIND) { dsymndx = (((uintptr_t)symdef - (uintptr_t)SYMTAB(_lmp)) / SYMENT(_lmp)); value = audit_symbind(lmp, _lmp, symdef, dsymndx, value, &sb_flags); } } /* * If relocation is PC-relative, subtract * offset address. */ if (IS_PC_RELATIVE(rtype)) value -= roffset; /* * Special case TLS relocations. */ if (rtype == R_386_TLS_DTPMOD32) { /* * Relocation value is the TLS modid. */ value = TLSMODID(_lmp); } else if (rtype == R_386_TLS_TPOFF) { if ((value = elf_static_tls(_lmp, symdef, rel, rtype, name, roffset, value)) == 0) { ret = 0; break; } } } } else { /* * Special cases. */ if (rtype == R_386_TLS_DTPMOD32) { /* * TLS relocation value is the TLS modid. */ value = TLSMODID(lmp); } else value = basebgn; name = NULL; } DBG_CALL(Dbg_reloc_in(LIST(lmp), ELF_DBG_RTLD, M_MACH, M_REL_SHT_TYPE, rel, NULL, 0, name)); /* * Make sure the segment is writable. */ if (((mpp->mr_prot & PROT_WRITE) == 0) && ((set_prot(lmp, mpp, 1) == 0) || (aplist_append(textrel, mpp, AL_CNT_TEXTREL) == NULL))) { ret = 0; break; } /* * Call relocation routine to perform required relocation. */ switch (rtype) { case R_386_COPY: if (elf_copy_reloc(name, symref, lmp, (void *)roffset, symdef, _lmp, (const void *)value) == 0) ret = 0; break; case R_386_JMP_SLOT: if (((LIST(lmp)->lm_tflags | AFLAGS(lmp)) & (LML_TFLG_AUD_PLTENTER | LML_TFLG_AUD_PLTEXIT)) && AUDINFO(lmp)->ai_dynplts) { int fail = 0; int pltndx = (((ulong_t)rel - (uintptr_t)JMPREL(lmp)) / relsiz); int symndx = (((uintptr_t)symdef - (uintptr_t)SYMTAB(_lmp)) / SYMENT(_lmp)); (void) elf_plt_trace_write(roffset, lmp, _lmp, symdef, symndx, pltndx, (caddr_t)value, sb_flags, &fail); if (fail) ret = 0; } else { /* * Write standard PLT entry to jump directly * to newly bound function. */ DBG_CALL(Dbg_reloc_apply_val(LIST(lmp), ELF_DBG_RTLD, (Xword)roffset, (Xword)value)); *(ulong_t *)roffset = value; } break; default: /* * Write the relocation out. */ if (do_reloc_rtld(rtype, (uchar_t *)roffset, (Word *)&value, name, NAME(lmp), LIST(lmp)) == 0) ret = 0; DBG_CALL(Dbg_reloc_apply_val(LIST(lmp), ELF_DBG_RTLD, (Xword)roffset, (Xword)value)); } if ((ret == 0) && ((LIST(lmp)->lm_flags & LML_FLG_TRC_WARN) == 0)) break; if (binfo) { DBG_CALL(Dbg_bind_global(lmp, (Addr)roffset, (Off)(roffset - basebgn), (Xword)(-1), PLT_T_FULL, _lmp, (Addr)value, symdef->st_value, name, binfo)); } } return (relocate_finish(lmp, bound, ret)); }
static int relocate_file(elf_file_t ef) { const Elf_Rel *rellim; const Elf_Rel *rel; const Elf_Rela *relalim; const Elf_Rela *rela; const char *symname; const Elf_Sym *sym; int i; Elf_Size symidx; Elf_Addr base; /* Perform relocations without addend if there are any: */ for (i = 0; i < ef->nreltab; i++) { rel = ef->reltab[i].rel; if (rel == NULL) panic("lost a reltab!"); rellim = rel + ef->reltab[i].nrel; base = findbase(ef, ef->reltab[i].sec); if (base == 0) panic("lost base for reltab"); for ( ; rel < rellim; rel++) { symidx = ELF_R_SYM(rel->r_info); if (symidx >= ef->ddbsymcnt) continue; sym = ef->ddbsymtab + symidx; /* Local relocs are already done */ if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) continue; if (elf_reloc(&ef->lf, base, rel, ELF_RELOC_REL, elf_obj_lookup)) { symname = symbol_name(ef, rel->r_info); printf("link_elf_obj: symbol %s undefined\n", symname); return ENOENT; } } } /* Perform relocations with addend if there are any: */ for (i = 0; i < ef->nrelatab; i++) { rela = ef->relatab[i].rela; if (rela == NULL) panic("lost a relatab!"); relalim = rela + ef->relatab[i].nrela; base = findbase(ef, ef->relatab[i].sec); if (base == 0) panic("lost base for relatab"); for ( ; rela < relalim; rela++) { symidx = ELF_R_SYM(rela->r_info); if (symidx >= ef->ddbsymcnt) continue; sym = ef->ddbsymtab + symidx; /* Local relocs are already done */ if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) continue; if (elf_reloc(&ef->lf, base, rela, ELF_RELOC_RELA, elf_obj_lookup)) { symname = symbol_name(ef, rela->r_info); printf("link_elf_obj: symbol %s undefined\n", symname); return ENOENT; } } } /* * Only clean SHN_FBSD_CACHED for successfull return. If we * modified symbol table for the object but found an * unresolved symbol, there is no reason to roll back. */ elf_obj_cleanup_globals_cache(ef); return 0; }
int __elf_fdnlist(int fd, struct nlist *list) { struct nlist *p; caddr_t strtab; Elf_Off symoff = 0, symstroff = 0; Elf_Word symsize = 0, symstrsize = 0; Elf_Sword nent, cc, i; Elf_Sym sbuf[1024]; Elf_Sym *s; Elf_Ehdr ehdr; Elf_Shdr *shdr = NULL; Elf_Word shdr_size; struct stat st; int usemalloc = 0; /* Make sure obj is OK */ if (pread(fd, &ehdr, sizeof(Elf_Ehdr), (off_t)0) != sizeof(Elf_Ehdr) || !__elf_is_okay__(&ehdr) || fstat(fd, &st) < 0) return (-1); /* calculate section header table size */ shdr_size = ehdr.e_shentsize * ehdr.e_shnum; /* Make sure it's not too big to mmap */ if (shdr_size > SIZE_T_MAX) { errno = EFBIG; return (-1); } /* mmap section header table */ shdr = (Elf_Shdr *)mmap(NULL, (size_t)shdr_size, PROT_READ, MAP_SHARED|MAP_FILE, fd, (off_t) ehdr.e_shoff); if (shdr == MAP_FAILED) { usemalloc = 1; if ((shdr = malloc(shdr_size)) == NULL) return (-1); if (pread(fd, shdr, shdr_size, (off_t)ehdr.e_shoff) != shdr_size) { free(shdr); return (-1); } } /* * Find the symbol table entry and its corresponding * string table entry. Version 1.1 of the ABI states * that there is only one symbol table but that this * could change in the future. */ for (i = 0; i < ehdr.e_shnum; i++) { if (shdr[i].sh_type == SHT_SYMTAB) { symoff = shdr[i].sh_offset; symsize = shdr[i].sh_size; symstroff = shdr[shdr[i].sh_link].sh_offset; symstrsize = shdr[shdr[i].sh_link].sh_size; break; } } /* Flush the section header table */ if (usemalloc) free(shdr); else munmap((caddr_t)shdr, shdr_size); /* Check for files too large to mmap. */ /* XXX is this really possible? */ if (symstrsize > SIZE_T_MAX) { errno = EFBIG; return (-1); } /* * Map string table into our address space. This gives us * an easy way to randomly access all the strings, without * making the memory allocation permanent as with malloc/free * (i.e., munmap will return it to the system). */ if (usemalloc) { if ((strtab = malloc(symstrsize)) == NULL) return (-1); if (pread(fd, strtab, symstrsize, (off_t)symstroff) != symstrsize) { free(strtab); return (-1); } } else { strtab = mmap(NULL, (size_t)symstrsize, PROT_READ, MAP_SHARED|MAP_FILE, fd, (off_t) symstroff); if (strtab == MAP_FAILED) return (-1); } /* * clean out any left-over information for all valid entries. * Type and value defined to be 0 if not found; historical * versions cleared other and desc as well. Also figure out * the largest string length so don't read any more of the * string table than we have to. * * XXX clearing anything other than n_type and n_value violates * the semantics given in the man page. */ nent = 0; for (p = list; !ISLAST(p); ++p) { p->n_type = 0; p->n_other = 0; p->n_desc = 0; p->n_value = 0; ++nent; } /* Don't process any further if object is stripped. */ /* ELFism - dunno if stripped by looking at header */ if (symoff == 0) goto elf_done; while (symsize > 0) { cc = MIN(symsize, sizeof(sbuf)); if (pread(fd, sbuf, cc, (off_t)symoff) != cc) break; symsize -= cc; symoff += cc; for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) { int soff = s->st_name; if (soff == 0) continue; for (p = list; !ISLAST(p); p++) { char *sym; /* * First we check for the symbol as it was * provided by the user. If that fails * and the first char is an '_', skip over * the '_' and try again. * XXX - What do we do when the user really * wants '_foo' and the are symbols * for both 'foo' and '_foo' in the * table and 'foo' is first? */ sym = p->n_un.n_name; if (strcmp(&strtab[soff], sym) != 0 && (sym[0] != '_' || strcmp(&strtab[soff], sym + 1) != 0)) continue; p->n_value = s->st_value; /* XXX - type conversion */ /* is pretty rude. */ switch(ELF_ST_TYPE(s->st_info)) { case STT_NOTYPE: switch (s->st_shndx) { case SHN_UNDEF: p->n_type = N_UNDF; break; case SHN_ABS: p->n_type = N_ABS; break; case SHN_COMMON: p->n_type = N_COMM; break; default: p->n_type = N_COMM | N_EXT; break; } break; case STT_OBJECT: p->n_type = N_DATA; break; case STT_FUNC: p->n_type = N_TEXT; break; case STT_FILE: p->n_type = N_FN; break; } if (ELF_ST_BIND(s->st_info) == STB_LOCAL) p->n_type = N_EXT; p->n_desc = 0; p->n_other = 0; if (--nent <= 0) break; } } } elf_done: if (usemalloc) free(strtab); else munmap(strtab, symstrsize); return (nent); }
uintptr_t ld_group_process(Is_desc *gisc, Ofl_desc *ofl) { Ifl_desc *gifl = gisc->is_file; Shdr *sshdr, *gshdr = gisc->is_shdr; Is_desc *isc; Sym *sym; const char *str; Group_desc gd; size_t ndx; int gnu_stt_section; /* * Confirm that the sh_link points to a valid section. */ if ((gshdr->sh_link == SHN_UNDEF) || (gshdr->sh_link >= gifl->ifl_shnum) || ((isc = gifl->ifl_isdesc[gshdr->sh_link]) == NULL)) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHLINK), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, EC_XWORD(gshdr->sh_link)); return (0); } if (gshdr->sh_entsize == 0) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHENTSIZE), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, EC_XWORD(gshdr->sh_entsize)); return (0); } /* * Get the associated symbol table. Sanity check the sh_info field * (which points to the signature symbol table entry) against the size * of the symbol table. */ sshdr = isc->is_shdr; sym = (Sym *)isc->is_indata->d_buf; if ((sshdr->sh_info == SHN_UNDEF) || (gshdr->sh_info >= (Word)(sshdr->sh_size / sshdr->sh_entsize)) || ((isc = gifl->ifl_isdesc[sshdr->sh_link]) == NULL)) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_FIL_INVSHINFO), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, EC_XWORD(gshdr->sh_info)); return (0); } sym += gshdr->sh_info; /* * Get the symbol name from the associated string table. */ str = (char *)isc->is_indata->d_buf; str += sym->st_name; /* * The GNU assembler can use section symbols as the signature symbol * as described by this comment in the gold linker (found via google): * * It seems that some versions of gas will create a section group * associated with a section symbol, and then fail to give a name * to the section symbol. In such a case, use the name of the * section. * * In order to support such objects, we do the same. */ gnu_stt_section = ((sym->st_name == 0) || (*str == '\0')) && (ELF_ST_TYPE(sym->st_info) == STT_SECTION); if (gnu_stt_section) str = gisc->is_name; /* * Generate a group descriptor. */ gd.gd_isc = gisc; gd.gd_oisc = NULL; gd.gd_name = str; gd.gd_data = gisc->is_indata->d_buf; gd.gd_cnt = gisc->is_indata->d_size / sizeof (Word); /* * If this group is a COMDAT group, validate the signature symbol. */ if ((gd.gd_data[0] & GRP_COMDAT) && !gnu_stt_section && ((ELF_ST_BIND(sym->st_info) == STB_LOCAL) || (sym->st_shndx == SHN_UNDEF))) { /* If section symbol, construct a printable name for it */ if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) { if (gisc->is_sym_name == NULL) (void) ld_stt_section_sym_name(gisc); if (gisc->is_sym_name != NULL) str = gisc->is_sym_name; } ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_GRP_INVALSYM), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, str); return (0); } /* * If the signature symbol is a name generated by the GNU compiler to * refer to a header, we need sloppy relocation. */ if (is_header_gensym(str)) { if ((ofl->ofl_flags1 & FLG_OF1_NRLXREL) == 0) ofl->ofl_flags1 |= FLG_OF1_RLXREL; DBG_CALL(Dbg_sec_gnu_comdat(ofl->ofl_lml, gisc, TRUE, (ofl->ofl_flags1 & FLG_OF1_RLXREL) != 0)); } /* * Validate the section indices within the group. If this is a COMDAT * group, mark each section as COMDAT. */ for (ndx = 1; ndx < gd.gd_cnt; ndx++) { Word gndx; if ((gndx = gd.gd_data[ndx]) >= gifl->ifl_shnum) { ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_GRP_INVALNDX), gifl->ifl_name, EC_WORD(gisc->is_scnndx), gisc->is_name, ndx, gndx); return (0); } if (gd.gd_data[0] & GRP_COMDAT) gifl->ifl_isdesc[gndx]->is_flags |= FLG_IS_COMDAT; } /* * If this is a COMDAT group, determine whether this group has already * been encountered, or whether this is the first instance of the group. */ if ((gd.gd_data[0] & GRP_COMDAT) && (gpavl_loaded(ofl, &gd) == S_ERROR)) return (S_ERROR); /* * Associate the group descriptor with this input file. */ if (alist_append(&(gifl->ifl_groups), &gd, sizeof (Group_desc), AL_CNT_IFL_GROUPS) == NULL) return (S_ERROR); return (1); }
/* * Devise nlist's type from Elf_Sym. * XXX this task is done as well in libc and kvm_mkdb. */ int elf2nlist(Elf_Sym *sym, const Elf_Ehdr *eh, const Elf_Shdr *shdr, const char *shstr, struct nlist *np) { u_int stt; const char *sn; int type; if (sym->st_shndx < eh->e_shnum) sn = shstr + shdr[sym->st_shndx].sh_name; else sn = NULL; switch (stt = ELF_ST_TYPE(sym->st_info)) { case STT_NOTYPE: case STT_OBJECT: type = elf_shn2type(eh, sym->st_shndx, sn); if (type < 0) { if (sn == NULL) np->n_other = '?'; else np->n_type = stt == STT_NOTYPE? N_COMM : N_DATA; } else { /* a hack for .rodata check (; */ if (type == N_SIZE) { np->n_type = N_DATA; np->n_other = 'r'; } else np->n_type = type; } break; case STT_FUNC: type = elf_shn2type(eh, sym->st_shndx, sn); np->n_type = type < 0? N_TEXT : type; if (type < 0) np->n_other = 't'; if (ELF_ST_BIND(sym->st_info) == STB_WEAK) { np->n_type = N_INDR; np->n_other = 'W'; } else if (sn != NULL && *sn != 0 && strcmp(sn, ELF_INIT) && strcmp(sn, ELF_TEXT) && strcmp(sn, ELF_FINI)) /* XXX GNU compat */ np->n_other = '?'; break; case STT_SECTION: type = elf_shn2type(eh, sym->st_shndx, NULL); if (type < 0) np->n_other = '?'; else np->n_type = type; break; case STT_FILE: np->n_type = N_FN | N_EXT; break; case STT_PARISC_MILLI: if (eh->e_machine == EM_PARISC) np->n_type = N_TEXT; else np->n_other = '?'; break; default: np->n_other = '?'; break; } if ((np->n_type & N_TYPE) != N_UNDF && ELF_ST_BIND(sym->st_info) != STB_LOCAL) { np->n_type |= N_EXT; if (np->n_other) np->n_other = toupper(np->n_other); } np->n_value = sym->st_value; np->n_un.n_strx = sym->st_name; return (0); }