Home | History | Annotate | Download | only in libelf
      1 /* Extract symbol list from binary.
      2    Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
      3    Written by Ulrich Drepper <drepper (at) redhat.com>, 1998.
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation, version 2.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12    GNU General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License
     15    along with this program; if not, write to the Free Software Foundation,
     16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
     17 
     18 #ifdef HAVE_CONFIG_H
     19 # include <config.h>
     20 #endif
     21 
     22 #include <fcntl.h>
     23 #include <gelf.h>
     24 #include <libelf.h>
     25 #include <nlist.h>
     26 #include <unistd.h>
     27 
     28 #include "libelfP.h"
     29 
     30 
     31 struct hashentry
     32 {
     33   const char *str;
     34   GElf_Sym sym;
     35 };
     36 #define TYPE struct hashentry
     37 /* XXX Use a better hash function some day.  */
     38 #define HASHFCT(str, len) INTUSE(elf_hash) (str)
     39 #define COMPARE(p1, p2) strcmp ((p1)->str, (p2)->str)
     40 #define CLASS static
     41 #define PREFIX nlist_
     42 #define xcalloc(n, m) calloc (n, m)
     43 #define next_prime(s) __libelf_next_prime (s)
     44 #include <fixedsizehash.h>
     45 
     46 
     47 int
     48 nlist (const char *filename, struct nlist *nl)
     49 {
     50   int fd;
     51   Elf *elf;
     52   Elf_Scn *scn = NULL;
     53   Elf_Scn *symscn = NULL;
     54   GElf_Shdr shdr_mem;
     55   GElf_Shdr *shdr = NULL;
     56   Elf_Data *data;
     57   struct nlist_fshash *table;
     58   size_t nsyms;
     59   size_t cnt;
     60 
     61   /* Open the file.  */
     62   fd = open (filename, O_RDONLY);
     63   if (fd == -1)
     64     {
     65       __libelf_seterrno (ELF_E_NOFILE);
     66       goto fail;
     67     }
     68 
     69   /* For compatibility reasons (`nlist' existed before ELF and libelf)
     70      we don't expect the caller to set the ELF version.  Do this here
     71      if it hasn't happened yet.  */
     72   if (__libelf_version_initialized == 0)
     73     INTUSE(elf_version) (EV_CURRENT);
     74 
     75   /* Now get an ELF descriptor.  */
     76   elf = INTUSE(elf_begin) (fd, ELF_C_READ_MMAP, NULL);
     77   if (elf == NULL)
     78     goto fail;
     79 
     80   /* Find a symbol table.  We prefer the real symbol table but if it
     81      does not exist use the dynamic symbol table.  */
     82   while ((scn = INTUSE(elf_nextscn) (elf, scn)) != NULL)
     83     {
     84       shdr = INTUSE(gelf_getshdr) (scn, &shdr_mem);
     85       if (shdr == NULL)
     86 	goto fail_close;
     87 
     88       /* That is what we are looking for.  */
     89       if (shdr->sh_type == SHT_SYMTAB)
     90 	{
     91 	  symscn = scn;
     92 	  break;
     93 	}
     94 
     95       /* Better than nothing.  Remember this section.  */
     96       if (shdr->sh_type == SHT_DYNSYM)
     97 	symscn = scn;
     98     }
     99 
    100   if (symscn == NULL)
    101     /* We haven't found anything.  Fail.  */
    102     goto fail_close;
    103 
    104   /* Re-get the section header in case we found only the dynamic symbol
    105      table.  */
    106   if (scn == NULL)
    107     shdr = INTUSE(gelf_getshdr) (symscn, &shdr_mem);
    108   /* SHDR->SH_LINK now contains the index of the string section.  */
    109 
    110   /* Get the data for the symbol section.  */
    111   data = INTUSE(elf_getdata) (symscn, NULL);
    112   if (data == NULL)
    113     goto fail_close;
    114 
    115   /* How many symbols are there?  */
    116   nsyms = (shdr->sh_size
    117 	   / INTUSE(gelf_fsize) (elf, ELF_T_SYM, 1, data->d_version));
    118 
    119   /* Create the hash table.  */
    120   table = nlist_fshash_init (nsyms);
    121   if (table == NULL)
    122     {
    123       __libelf_seterrno (ELF_E_NOMEM);
    124       goto fail_close;
    125     }
    126 
    127   /* Iterate over all the symbols in the section.  */
    128   for (cnt = 0; cnt < nsyms; ++cnt)
    129     {
    130       struct hashentry mem;
    131       GElf_Sym *sym;
    132 
    133       /* Get the symbol.  */
    134       sym = INTUSE(gelf_getsym) (data, cnt, &mem.sym);
    135       if (sym == NULL)
    136 	goto fail_dealloc;
    137 
    138       /* Get the name of the symbol.  */
    139       mem.str = INTUSE(elf_strptr) (elf, shdr->sh_link, sym->st_name);
    140       if (mem.str == NULL)
    141 	goto fail_dealloc;
    142 
    143       /* Don't allow zero-length strings.  */
    144       if (mem.str[0] == '\0')
    145 	continue;
    146 
    147       /* And add it to the hash table.  Note that we are using the
    148          overwrite version.  This will ensure that
    149 	 a) global symbols are preferred over local symbols since
    150 	    they are all located at the end
    151 	 b) if there are multiple local symbols with the same name
    152 	    the last one is used.
    153       */
    154       (void) nlist_fshash_overwrite (table, mem.str, 0, &mem);
    155     }
    156 
    157   /* Now it is time to look for the symbols the user asked for.
    158      XXX What is a `null name/null string'?  This is what the
    159      standard says terminates the list.  Is it a null pointer
    160      or a zero-length string?  We test for both...  */
    161   while (nl->n_name != NULL && nl->n_name[0] != '\0')
    162     {
    163       struct hashentry search;
    164       const struct hashentry *found;
    165 
    166       /* Search for a matching entry in the hash table.  */
    167       search.str = nl->n_name;
    168       found = nlist_fshash_find (table, nl->n_name, 0, &search);
    169 
    170       if (found != NULL)
    171 	{
    172 	  /* Found it.  */
    173 	  nl->n_value = found->sym.st_value;
    174 	  nl->n_scnum = found->sym.st_shndx;
    175 	  nl->n_type = GELF_ST_TYPE (found->sym.st_info);
    176 	  /* XXX What shall we fill in the next fields?  */
    177 	  nl->n_sclass = 0;
    178 	  nl->n_numaux = 0;
    179 	}
    180       else
    181 	{
    182 	  /* Not there.  */
    183 	  nl->n_value = 0;
    184 	  nl->n_scnum = 0;
    185 	  nl->n_type = 0;
    186 	  nl->n_sclass = 0;
    187 	  nl->n_numaux = 0;
    188 	}
    189 
    190       /* Next search request.  */
    191       ++nl;
    192     }
    193 
    194   /* Free the resources.  */
    195   nlist_fshash_fini (table);
    196 
    197   /* We do not need the ELF descriptor anymore.  */
    198   (void) elf_end (elf);
    199 
    200   return 0;
    201 
    202  fail_dealloc:
    203   nlist_fshash_fini (table);
    204 
    205  fail_close:
    206   /* We do not need the ELF descriptor anymore.  */
    207   (void) elf_end (elf);
    208 
    209  fail:
    210   /* We have to set all entries to zero.  */
    211   while (nl->n_name != NULL && nl->n_name[0] != '\0')
    212     {
    213       nl->n_value = 0;
    214       nl->n_scnum = 0;
    215       nl->n_type = 0;
    216       nl->n_sclass = 0;
    217       nl->n_numaux = 0;
    218 
    219       /* Next entry.  */
    220       ++nl;
    221     }
    222 
    223   return -1;
    224 }
    225