/* This file is part of centaur. * * centaur is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License 2 as * published by the Free Software Foundation. * centaur is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with centaur. If not, see . */ #include #include #include #include int elfu_mSymtabLookupSymToAddr(ElfuElf *me, ElfuScn *msst, ElfuSym *sym, GElf_Addr *result) { char *symname = ELFU_SYMSTR(msst, sym->name); switch (sym->type) { case STT_NOTYPE: case STT_OBJECT: case STT_FUNC: if (sym->shndx == SHN_UNDEF) { return -1; } else if (sym->shndx == SHN_ABS) { *result = sym->value; return 0; } else if (sym->shndx == SHN_COMMON) { return -1; } else { ElfuScn *newscn; assert (sym->scnptr); newscn = elfu_mScnByOldscn(me, sym->scnptr); assert(newscn); *result = newscn->shdr.sh_addr + sym->value; return 0; } break; case STT_SECTION: assert(sym->scnptr); assert(elfu_mScnByOldscn(me, sym->scnptr)); *result = elfu_mScnByOldscn(me, sym->scnptr)->shdr.sh_addr; return 0; case STT_FILE: ELFU_WARN("elfu_mSymtabLookupSymToAddr: Symbol type FILE is not supported.\n"); return -1; default: ELFU_WARN("elfu_mSymtabLookupSymToAddr: Unknown symbol type %d for %s.\n", sym->type, symname); return -1; } } char* elfu_mSymtabSymToName(ElfuScn *msst, ElfuSym *sym) { assert(msst); assert(sym); return ELFU_SYMSTR(msst, sym->name); } ElfuSym* elfu_mSymtabIndexToSym(ElfuScn *msst, GElf_Word entry) { GElf_Word i; ElfuSym *sym; assert(msst); assert(entry > 0); assert(!CIRCLEQ_EMPTY(&msst->symtab.syms)); sym = CIRCLEQ_FIRST(&msst->symtab.syms); for (i = 1; i < entry; i++) { sym = CIRCLEQ_NEXT(sym, elem); } return sym; } /* Look up a value in the symbol table section *msst which is in *me. */ GElf_Addr elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name) { ElfuSym *sym; assert(me); assert(msst); assert(name); assert(strlen(name) > 0); assert(!CIRCLEQ_EMPTY(&msst->symtab.syms)); CIRCLEQ_FOREACH(sym, &msst->symtab.syms, elem) { char *symname = ELFU_SYMSTR(msst, sym->name); if (!strcmp(symname, name)) { goto SYMBOL_FOUND; } } return 0; SYMBOL_FOUND: switch (sym->type) { case STT_NOTYPE: case STT_OBJECT: case STT_FUNC: if (sym->scnptr) { GElf_Addr a = sym->value; a += me->ehdr.e_type == ET_REL ? sym->scnptr->shdr.sh_addr : 0; return a; } else if (sym->shndx == SHN_UNDEF) { return 0; } else if (sym->shndx == SHN_ABS) { return sym->value; } else { ELFU_WARN("elfu_mSymtabLookupAddrByName: Symbol binding COMMON is not supported, using 0.\n"); return 0; } break; default: return 0; } } /* Convert symtab from memory model to elfclass specific format */ void elfu_mSymtabFlatten(ElfuElf *me) { ElfuSym *sym; size_t numsyms = 0; elfu_mLayoutAuto(me); /* Update section indexes and count symbols */ CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) { if (sym->scnptr) { sym->shndx = elfu_mScnIndex(me, sym->scnptr); } numsyms++; } /* Copy symbols to elfclass-specific format */ if (me->elfclass == ELFCLASS32) { size_t newsize = (numsyms + 1) * sizeof(Elf32_Sym); size_t i; if (me->symtab->databuf) { free(me->symtab->databuf); } me->symtab->databuf = malloc(newsize); assert(me->symtab->databuf); me->symtab->shdr.sh_size = newsize; memset(me->symtab->databuf, 0, newsize); i = 1; CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) { Elf32_Sym *es = ((Elf32_Sym*)me->symtab->databuf) + i; es->st_name = sym->name; es->st_value = sym->value; es->st_size = sym->size; es->st_info = ELF32_ST_INFO(sym->bind, sym->type); es->st_other = sym->other; es->st_shndx = sym->shndx; i++; } } else if (me->elfclass == ELFCLASS64) { size_t newsize = (numsyms + 1) * sizeof(Elf64_Sym); size_t i; if (me->symtab->databuf) { free(me->symtab->databuf); } me->symtab->databuf = malloc(newsize); assert(me->symtab->databuf); me->symtab->shdr.sh_size = newsize; memset(me->symtab->databuf, 0, newsize); i = 1; CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) { Elf64_Sym *es = ((Elf64_Sym*)me->symtab->databuf) + i; es->st_name = sym->name; es->st_value = sym->value; es->st_size = sym->size; es->st_info = ELF64_ST_INFO(sym->bind, sym->type); es->st_other = sym->other; es->st_shndx = sym->shndx; i++; } } else { /* Unknown elfclass */ assert(0); } elfu_mLayoutAuto(me); } void elfu_mSymtabAddGlobalDymtabIfNotPresent(ElfuElf *me) { assert(me); if (!me->symtab) { ElfuScn *symtab; ElfuScn *strtab; symtab = elfu_mScnAlloc(); assert(symtab); strtab = elfu_mScnAlloc(); assert(strtab); symtab->linkptr = strtab; symtab->shdr.sh_entsize = me->elfclass == ELFCLASS32 ? sizeof(Elf32_Sym) : sizeof(Elf64_Sym); symtab->shdr.sh_addralign = 4; strtab->shdr.sh_addralign = 1; symtab->shdr.sh_type = SHT_SYMTAB; strtab->shdr.sh_type = SHT_STRTAB; strtab->databuf = malloc(1); assert(strtab->databuf); strtab->databuf[0] = 0; CIRCLEQ_INSERT_TAIL(&me->orphanScnList, symtab, elemChildScn); CIRCLEQ_INSERT_TAIL(&me->orphanScnList, strtab, elemChildScn); me->symtab = symtab; if (me->shstrtab) { symtab->shdr.sh_name = me->shstrtab->shdr.sh_size; if (elfu_mScnAppendData(me->shstrtab, ".symtab", 8)) { symtab->shdr.sh_name = 0; } strtab->shdr.sh_name = me->shstrtab->shdr.sh_size; if (elfu_mScnAppendData(me->shstrtab, ".strtab", 8)) { strtab->shdr.sh_name = 0; } } } }