diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:28 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:29:28 -0800 |
commit | 441f72d43a9b550baa779fc82f70816da5f74f0e (patch) | |
tree | 477ac2b54c1ef5b8e25e4255b27bca9469ed3911 /libelf/nlist.c | |
parent | 7a80622f69812ca3262d2027c07a4ed0c0242945 (diff) | |
download | android_external_elfutils-441f72d43a9b550baa779fc82f70816da5f74f0e.tar.gz android_external_elfutils-441f72d43a9b550baa779fc82f70816da5f74f0e.tar.bz2 android_external_elfutils-441f72d43a9b550baa779fc82f70816da5f74f0e.zip |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'libelf/nlist.c')
-rw-r--r-- | libelf/nlist.c | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/libelf/nlist.c b/libelf/nlist.c new file mode 100644 index 00000000..cc42a31e --- /dev/null +++ b/libelf/nlist.c @@ -0,0 +1,224 @@ +/* Extract symbol list from binary. + Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 1998. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 2. + + This program 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 this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <fcntl.h> +#include <gelf.h> +#include <libelf.h> +#include <nlist.h> +#include <unistd.h> + +#include "libelfP.h" + + +struct hashentry +{ + const char *str; + GElf_Sym sym; +}; +#define TYPE struct hashentry +/* XXX Use a better hash function some day. */ +#define HASHFCT(str, len) INTUSE(elf_hash) (str) +#define COMPARE(p1, p2) strcmp ((p1)->str, (p2)->str) +#define CLASS static +#define PREFIX nlist_ +#define xcalloc(n, m) calloc (n, m) +#define next_prime(s) __libelf_next_prime (s) +#include <fixedsizehash.h> + + +int +nlist (const char *filename, struct nlist *nl) +{ + int fd; + Elf *elf; + Elf_Scn *scn = NULL; + Elf_Scn *symscn = NULL; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = NULL; + Elf_Data *data; + struct nlist_fshash *table; + size_t nsyms; + size_t cnt; + + /* Open the file. */ + fd = open (filename, O_RDONLY); + if (fd == -1) + { + __libelf_seterrno (ELF_E_NOFILE); + goto fail; + } + + /* For compatibility reasons (`nlist' existed before ELF and libelf) + we don't expect the caller to set the ELF version. Do this here + if it hasn't happened yet. */ + if (__libelf_version_initialized == 0) + INTUSE(elf_version) (EV_CURRENT); + + /* Now get an ELF descriptor. */ + elf = INTUSE(elf_begin) (fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + goto fail; + + /* Find a symbol table. We prefer the real symbol table but if it + does not exist use the dynamic symbol table. */ + while ((scn = INTUSE(elf_nextscn) (elf, scn)) != NULL) + { + shdr = INTUSE(gelf_getshdr) (scn, &shdr_mem); + if (shdr == NULL) + goto fail_close; + + /* That is what we are looking for. */ + if (shdr->sh_type == SHT_SYMTAB) + { + symscn = scn; + break; + } + + /* Better than nothing. Remember this section. */ + if (shdr->sh_type == SHT_DYNSYM) + symscn = scn; + } + + if (symscn == NULL) + /* We haven't found anything. Fail. */ + goto fail_close; + + /* Re-get the section header in case we found only the dynamic symbol + table. */ + if (scn == NULL) + shdr = INTUSE(gelf_getshdr) (symscn, &shdr_mem); + /* SHDR->SH_LINK now contains the index of the string section. */ + + /* Get the data for the symbol section. */ + data = INTUSE(elf_getdata) (symscn, NULL); + if (data == NULL) + goto fail_close; + + /* How many symbols are there? */ + nsyms = (shdr->sh_size + / INTUSE(gelf_fsize) (elf, ELF_T_SYM, 1, data->d_version)); + + /* Create the hash table. */ + table = nlist_fshash_init (nsyms); + if (table == NULL) + { + __libelf_seterrno (ELF_E_NOMEM); + goto fail_close; + } + + /* Iterate over all the symbols in the section. */ + for (cnt = 0; cnt < nsyms; ++cnt) + { + struct hashentry mem; + GElf_Sym *sym; + + /* Get the symbol. */ + sym = INTUSE(gelf_getsym) (data, cnt, &mem.sym); + if (sym == NULL) + goto fail_dealloc; + + /* Get the name of the symbol. */ + mem.str = INTUSE(elf_strptr) (elf, shdr->sh_link, sym->st_name); + if (mem.str == NULL) + goto fail_dealloc; + + /* Don't allow zero-length strings. */ + if (mem.str[0] == '\0') + continue; + + /* And add it to the hash table. Note that we are using the + overwrite version. This will ensure that + a) global symbols are preferred over local symbols since + they are all located at the end + b) if there are multiple local symbols with the same name + the last one is used. + */ + (void) nlist_fshash_overwrite (table, mem.str, 0, &mem); + } + + /* Now it is time to look for the symbols the user asked for. + XXX What is a `null name/null string'? This is what the + standard says terminates the list. Is it a null pointer + or a zero-length string? We test for both... */ + while (nl->n_name != NULL && nl->n_name[0] != '\0') + { + struct hashentry search; + const struct hashentry *found; + + /* Search for a matching entry in the hash table. */ + search.str = nl->n_name; + found = nlist_fshash_find (table, nl->n_name, 0, &search); + + if (found != NULL) + { + /* Found it. */ + nl->n_value = found->sym.st_value; + nl->n_scnum = found->sym.st_shndx; + nl->n_type = GELF_ST_TYPE (found->sym.st_info); + /* XXX What shall we fill in the next fields? */ + nl->n_sclass = 0; + nl->n_numaux = 0; + } + else + { + /* Not there. */ + nl->n_value = 0; + nl->n_scnum = 0; + nl->n_type = 0; + nl->n_sclass = 0; + nl->n_numaux = 0; + } + + /* Next search request. */ + ++nl; + } + + /* Free the resources. */ + nlist_fshash_fini (table); + + /* We do not need the ELF descriptor anymore. */ + (void) elf_end (elf); + + return 0; + + fail_dealloc: + nlist_fshash_fini (table); + + fail_close: + /* We do not need the ELF descriptor anymore. */ + (void) elf_end (elf); + + fail: + /* We have to set all entries to zero. */ + while (nl->n_name != NULL && nl->n_name[0] != '\0') + { + nl->n_value = 0; + nl->n_scnum = 0; + nl->n_type = 0; + nl->n_sclass = 0; + nl->n_numaux = 0; + + /* Next entry. */ + ++nl; + } + + return -1; +} |