基本思路是分析elf文件,
首先遍历节区头部Elf32_Shdr查看sh_type属性值,得到属性值为SHT_DYNSYM的节区。
其次通过名字遍历节区结点,找到类型为STT_FUNC并且名字与其相同的结点。
代码如下:
static void *
xmalloc(size_t size)
{
void *p;
p = malloc(size);
if (!p) {
LOGV("Out of memory");
exit(1);
}
return p;
}
static struct symlist *
get_syms(int fd, Elf64_Shdr *symh, Elf64_Shdr *strh)
{
struct symlist *sl, *ret;
int rv;
ret = NULL;
sl = (struct symlist *) xmalloc(sizeof(struct symlist));
sl->str = NULL;
sl->sym = NULL;
/* sanity */
if (symh->sh_size % sizeof(Elf64_Sym)) {
LOGV("elf_error");
goto out;
}
/* symbol table */
sl->num = symh->sh_size / sizeof(Elf64_Sym);
sl->sym = (Elf64_Sym *) xmalloc(symh->sh_size);
rv = pread(fd, sl->sym, symh->sh_size, symh->sh_offset);
if (0 > rv) {
//perror("read");
goto out;
}
if (rv != symh->sh_size) {
LOGV("elf error");
goto out;
}
/* string table */
sl->str = (char *) xmalloc(strh->sh_size);
rv = pread(fd, sl->str, strh->sh_size, strh->sh_offset);
if (0 > rv) {
//perror("read");
goto out;
}
if (rv != strh->sh_size) {
LOGV("elf error");
goto out;
}
ret = sl;
out:
return ret;
}
static int
do_load(int fd, symtab_t symtab)
{
int rv;
size_t size;
Elf64_Ehdr ehdr;
Elf64_Shdr *shdr = NULL, *p;
Elf64_Shdr *dynsymh, *dynstrh;
Elf64_Shdr *symh, *strh;
char *shstrtab = NULL;
int i;
int ret = -1;
/* elf header */
rv = read(fd, &ehdr, sizeof(ehdr));
if (0 > rv) {
//perror("read");
goto out;
}
if (rv != sizeof(ehdr)) {
LOGV("elf error");
goto out;
}
if (strncmp(ELFMAG, ehdr.e_ident, SELFMAG)) { /* sanity */
LOGV("not an elf");
goto out;
}
if (sizeof(Elf64_Shdr) != ehdr.e_shentsize) { /* sanity */
LOGV("elf error");
goto out;
}
/* section header table */
size = ehdr.e_shentsize * ehdr.e_shnum;
shdr = (Elf64_Shdr *) xmalloc(size);
rv = pread(fd, shdr, size, ehdr.e_shoff);
if (0 > rv) {
//perror("read");
goto out;
}
if (rv != size) {
LOGV("elf error");
goto out;
}
/* section header string table */
size = shdr[ehdr.e_shstrndx].sh_size;
shstrtab = (char *) xmalloc(size);
rv = pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset);
if (0 > rv) {
//perror("read");
goto out;
}
if (rv != size) {
LOGV("elf error");
goto out;
}
/* symbol table headers */
symh = dynsymh = NULL;
strh = dynstrh = NULL;
for (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++)
if (SHT_SYMTAB == p->sh_type) {
if (symh) {
LOGV("too many symbol tables");
goto out;
}
symh = p;
} else if (SHT_DYNSYM == p->sh_type) {
if (dynsymh) {
LOGV("too many symbol tables");
goto out;
}
dynsymh = p;
} else if (SHT_STRTAB == p->sh_type
&& !strncmp(shstrtab+p->sh_name, ".strtab", 7)) {
if (strh) {
LOGV("too many string tables");
goto out;
}
strh = p;
} else if (SHT_STRTAB == p->sh_type
&& !strncmp(shstrtab+p->sh_name, ".dynstr", 7)) {
if (dynstrh) {
LOGV("too many string tables");
goto out;
}
dynstrh = p;
}
/* sanity checks */
if ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) {
LOGV("bad dynamic symbol table");
goto out;
}
if ((!symh && strh) || (symh && !strh)) {
LOGV("bad symbol table");
goto out;
}
if (!dynsymh && !symh) {
LOGV("no symbol table");
goto out;
}
/* symbol tables */
if (dynsymh)
symtab->dyn = get_syms(fd, dynsymh, dynstrh);
if (symh)
symtab->st = get_syms(fd, symh, strh);
ret = 0;
out:
free(shstrtab);
free(shdr);
return ret;
}
static symtab_t
load_symtab(char *filename)
{
int fd;
symtab_t symtab;
symtab = (symtab_t) xmalloc(sizeof(*symtab));
memset(symtab, 0, sizeof(*symtab));
fd = open(filename, O_RDONLY);
if (0 > fd) {
//perror("open");
return NULL;
}
if (0 > do_load(fd, symtab)) {
LOGV("Error ELF parsing %s", filename);
free(symtab);
symtab = NULL;
}
close(fd);
return symtab;
}
static int
lookup2(struct symlist *sl, unsigned char type,
char *name, unsigned long *val)
{
Elf64_Sym *p;
int len;
int i;
len = strlen(name);
for (i = 0, p = sl->sym; i < sl->num; i++, p++) {
//LOGV("name:%s lookup2name: %s %x", name, sl->str+p->st_name, p->st_value);
if (!strcmp(sl->str+p->st_name, name)
&& ELF64_ST_TYPE(p->st_info) == type) {
//if (p->st_value != 0) {
*val = p->st_value;
return 0;
//}
}
}
return -1;
}
static int
lookup_sym(symtab_t s, unsigned char type,
char *name, unsigned long *val)
{
if (s->dyn && !lookup2(s->dyn, type, name, val))
return 0;
if (s->st && !lookup2(s->st, type, name, val))
return 0;
return -1;
}
static int
lookup_func_sym(symtab_t s, char *name, unsigned long *val)
{
return lookup_sym(s, STT_FUNC, name, val);
}
static int
find_name(char* libname, char* fucname, unsigned long *addr)
{
symtab_t s;
s = load_symtab(libname);
if(!s)
{
LOGV("can't read symbol table %s", libname);
return -1;
}
if (0 > lookup_func_sym(s, fucname, addr)) {
LOGV("cannot find %s", fucname);
return -1;
}
return 0;
}