rpm/libelf/nlist.c

171 lines
3.9 KiB
C
Executable File

/*
nlist.c - implementation of the nlist(3) function.
Copyright (C) 1995 Michael Riepe <riepe@ifwsn4.ifw.uni-hannover.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <private.h>
#include <nlist.h>
#include <fcntl.h>
struct hash {
const char* name;
unsigned long hash;
Elf32_Sym* sym;
};
static int
_elf_nlist(Elf *elf, struct nlist *nl) {
Elf_Scn *symtab = NULL;
Elf_Scn *strtab = NULL;
Elf_Data *symdata;
Elf_Data *strdata;
Elf32_Ehdr *ehdr;
Elf32_Shdr *shdr;
Elf_Scn *scn;
Elf32_Sym *symbols;
const char *strings;
unsigned nsymbols;
unsigned nstrings;
unsigned i;
const char *name;
struct hash *table;
unsigned nhash;
unsigned long hash;
unsigned long j;
if (!(ehdr = elf32_getehdr(elf))) {
return -1;
}
scn = NULL;
while ((scn = elf_nextscn(elf, scn))) {
if (!(shdr = elf32_getshdr(scn))) {
return -1;
}
if (shdr->sh_type == SHT_SYMTAB) {
strtab = elf_getscn(elf, shdr->sh_link);
symtab = scn;
break;
}
if (shdr->sh_type == SHT_DYNSYM) {
strtab = elf_getscn(elf, shdr->sh_link);
symtab = scn;
}
}
if (elf_errno()) {
return -1;
}
symdata = elf_getdata(symtab, NULL);
strdata = elf_getdata(strtab, NULL);
if (!symdata || !strdata) {
return -1;
}
symbols = (Elf32_Sym*)symdata->d_buf;
strings = (const char*)strdata->d_buf;
nsymbols = symdata->d_size / sizeof(Elf32_Sym);
nstrings = strdata->d_size;
if (!symbols || !strings || !nsymbols || !nstrings) {
return -1;
}
/*
* build a simple hash table
*/
nhash = 3 * nsymbols - 4;
if (!(table = (struct hash*)malloc(nhash * sizeof(*table)))) {
return -1;
}
for (i = 0; i < nhash; i++) {
table[i].name = NULL;
}
for (i = 1; i < nsymbols; i++) {
if (symbols[i].st_name < 0 && symbols[i].st_name >= nstrings) {
free(table);
return -1;
}
if (symbols[i].st_name == 0) {
continue;
}
name = strings + symbols[i].st_name;
hash = elf_hash(name);
for (j = hash; table[j %= nhash].name; j += 3) {
if (table[j].hash != hash) {
continue;
}
if (table[j].name == name || !strcmp(table[j].name, name)) {
break;
}
}
table[j].hash = hash;
table[j].name = name;
table[j].sym = &symbols[i];
}
/*
* symbol lookup
*/
for (i = 0; (name = nl[i].n_name) && *name; i++) {
hash = elf_hash(name);
for (j = hash; table[j %= nhash].name; j += 3) {
if (table[j].hash == hash && !strcmp(table[j].name, name)) {
break;
}
}
if (table[j].name) {
nl[i].n_value = table[j].sym->st_value;
nl[i].n_scnum = table[j].sym->st_shndx;
}
else {
nl[i].n_value = 0;
nl[i].n_scnum = 0;
}
/*
* this needs more work
*/
nl[i].n_type = 0;
nl[i].n_sclass = 0;
nl[i].n_numaux = 0;
}
free(table);
return 0;
}
int
nlist(const char *filename, struct nlist *nl) {
int result = -1;
unsigned oldver;
Elf *elf;
int fd;
if ((oldver = elf_version(EV_CURRENT)) != EV_NONE) {
if ((fd = open(filename, O_RDONLY)) != -1) {
if ((elf = elf_begin(fd, ELF_C_READ, NULL))) {
result = _elf_nlist(elf, nl);
elf_end(elf);
}
close(fd);
}
elf_version(oldver);
}
if (result) {
while (nl->n_name && *nl->n_name) {
nl->n_value = 0;
nl++;
}
}
return result;
}