Home | History | Annotate | Download | only in libelf
      1 /* Extract symbol list from binary.
      2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005, 2007 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4    Written by Ulrich Drepper <drepper (at) redhat.com>, 1998.
      5 
      6    Red Hat elfutils is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by the
      8    Free Software Foundation; version 2 of the License.
      9 
     10    Red Hat elfutils is distributed in the hope that it will be useful, but
     11    WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License along
     16    with Red Hat elfutils; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     18 
     19    In addition, as a special exception, Red Hat, Inc. gives You the
     20    additional right to link the code of Red Hat elfutils with code licensed
     21    under any Open Source Initiative certified open source license
     22    (http://www.opensource.org/licenses/index.php) which requires the
     23    distribution of source code with any binary distribution and to
     24    distribute linked combinations of the two.  Non-GPL Code permitted under
     25    this exception must only link to the code of Red Hat elfutils through
     26    those well defined interfaces identified in the file named EXCEPTION
     27    found in the source code files (the "Approved Interfaces").  The files
     28    of Non-GPL Code may instantiate templates or use macros or inline
     29    functions from the Approved Interfaces without causing the resulting
     30    work to be covered by the GNU General Public License.  Only Red Hat,
     31    Inc. may make changes or additions to the list of Approved Interfaces.
     32    Red Hat's grant of this exception is conditioned upon your not adding
     33    any new exceptions.  If you wish to add a new Approved Interface or
     34    exception, please contact Red Hat.  You must obey the GNU General Public
     35    License in all respects for all of the Red Hat elfutils code and other
     36    code used in conjunction with Red Hat elfutils except the Non-GPL Code
     37    covered by this exception.  If you modify this file, you may extend this
     38    exception to your version of the file, but you are not obligated to do
     39    so.  If you do not wish to provide this exception without modification,
     40    you must delete this exception statement from your version and license
     41    this file solely under the GPL without exception.
     42 
     43    Red Hat elfutils is an included package of the Open Invention Network.
     44    An included package of the Open Invention Network is a package for which
     45    Open Invention Network licensees cross-license their patents.  No patent
     46    license is granted, either expressly or impliedly, by designation as an
     47    included package.  Should you wish to participate in the Open Invention
     48    Network licensing program, please visit www.openinventionnetwork.com
     49    <http://www.openinventionnetwork.com>.  */
     50 
     51 #ifdef HAVE_CONFIG_H
     52 # include <config.h>
     53 #endif
     54 
     55 #include <fcntl.h>
     56 #include <gelf.h>
     57 #include <libelf.h>
     58 #include <nlist.h>
     59 #include <unistd.h>
     60 
     61 #include "libelfP.h"
     62 
     63 
     64 struct hashentry
     65 {
     66   const char *str;
     67   GElf_Sym sym;
     68 };
     69 #define TYPE struct hashentry
     70 /* XXX Use a better hash function some day.  */
     71 #define HASHFCT(str, len) INTUSE(elf_hash) (str)
     72 #define COMPARE(p1, p2) strcmp ((p1)->str, (p2)->str)
     73 #define CLASS static
     74 #define PREFIX nlist_
     75 #define xcalloc(n, m) calloc (n, m)
     76 #define next_prime(s) __libelf_next_prime (s)
     77 #include <fixedsizehash.h>
     78 
     79 
     80 int
     81 nlist (const char *filename, struct nlist *nl)
     82 {
     83   int fd;
     84   Elf *elf;
     85   Elf_Scn *scn = NULL;
     86   Elf_Scn *symscn = NULL;
     87   GElf_Shdr shdr_mem;
     88   GElf_Shdr *shdr = NULL;
     89   Elf_Data *data;
     90   struct nlist_fshash *table;
     91   size_t nsyms;
     92   size_t cnt;
     93 
     94   /* Open the file.  */
     95   fd = open (filename, O_RDONLY);
     96   if (fd == -1)
     97     {
     98       __libelf_seterrno (ELF_E_NOFILE);
     99       goto fail;
    100     }
    101 
    102   /* For compatibility reasons (`nlist' existed before ELF and libelf)
    103      we don't expect the caller to set the ELF version.  Do this here
    104      if it hasn't happened yet.  */
    105   if (__libelf_version_initialized == 0)
    106     INTUSE(elf_version) (EV_CURRENT);
    107 
    108   /* Now get an ELF descriptor.  */
    109   elf = INTUSE(elf_begin) (fd, ELF_C_READ_MMAP, NULL);
    110   if (elf == NULL)
    111     goto fail_fd;
    112 
    113   /* Find a symbol table.  We prefer the real symbol table but if it
    114      does not exist use the dynamic symbol table.  */
    115   while ((scn = INTUSE(elf_nextscn) (elf, scn)) != NULL)
    116     {
    117       shdr = INTUSE(gelf_getshdr) (scn, &shdr_mem);
    118       if (shdr == NULL)
    119 	goto fail_close;
    120 
    121       /* That is what we are looking for.  */
    122       if (shdr->sh_type == SHT_SYMTAB)
    123 	{
    124 	  symscn = scn;
    125 	  break;
    126 	}
    127 
    128       /* Better than nothing.  Remember this section.  */
    129       if (shdr->sh_type == SHT_DYNSYM)
    130 	symscn = scn;
    131     }
    132 
    133   if (symscn == NULL)
    134     /* We haven't found anything.  Fail.  */
    135     goto fail_close;
    136 
    137   /* Re-get the section header in case we found only the dynamic symbol
    138      table.  */
    139   if (scn == NULL)
    140     shdr = INTUSE(gelf_getshdr) (symscn, &shdr_mem);
    141   /* SHDR->SH_LINK now contains the index of the string section.  */
    142 
    143   /* Get the data for the symbol section.  */
    144   data = INTUSE(elf_getdata) (symscn, NULL);
    145   if (data == NULL)
    146     goto fail_close;
    147 
    148   /* How many symbols are there?  */
    149   nsyms = (shdr->sh_size
    150 	   / INTUSE(gelf_fsize) (elf, ELF_T_SYM, 1, data->d_version));
    151 
    152   /* Create the hash table.  */
    153   table = nlist_fshash_init (nsyms);
    154   if (table == NULL)
    155     {
    156       __libelf_seterrno (ELF_E_NOMEM);
    157       goto fail_close;
    158     }
    159 
    160   /* Iterate over all the symbols in the section.  */
    161   for (cnt = 0; cnt < nsyms; ++cnt)
    162     {
    163       struct hashentry mem;
    164       GElf_Sym *sym;
    165 
    166       /* Get the symbol.  */
    167       sym = INTUSE(gelf_getsym) (data, cnt, &mem.sym);
    168       if (sym == NULL)
    169 	goto fail_dealloc;
    170 
    171       /* Get the name of the symbol.  */
    172       mem.str = INTUSE(elf_strptr) (elf, shdr->sh_link, sym->st_name);
    173       if (mem.str == NULL)
    174 	goto fail_dealloc;
    175 
    176       /* Don't allow zero-length strings.  */
    177       if (mem.str[0] == '\0')
    178 	continue;
    179 
    180       /* And add it to the hash table.  Note that we are using the
    181          overwrite version.  This will ensure that
    182 	 a) global symbols are preferred over local symbols since
    183 	    they are all located at the end
    184 	 b) if there are multiple local symbols with the same name
    185 	    the last one is used.
    186       */
    187       (void) nlist_fshash_overwrite (table, mem.str, 0, &mem);
    188     }
    189 
    190   /* Now it is time to look for the symbols the user asked for.
    191      XXX What is a `null name/null string'?  This is what the
    192      standard says terminates the list.  Is it a null pointer
    193      or a zero-length string?  We test for both...  */
    194   while (nl->n_name != NULL && nl->n_name[0] != '\0')
    195     {
    196       struct hashentry search;
    197       const struct hashentry *found;
    198 
    199       /* Search for a matching entry in the hash table.  */
    200       search.str = nl->n_name;
    201       found = nlist_fshash_find (table, nl->n_name, 0, &search);
    202 
    203       if (found != NULL)
    204 	{
    205 	  /* Found it.  */
    206 	  nl->n_value = found->sym.st_value;
    207 	  nl->n_scnum = found->sym.st_shndx;
    208 	  nl->n_type = GELF_ST_TYPE (found->sym.st_info);
    209 	  /* XXX What shall we fill in the next fields?  */
    210 	  nl->n_sclass = 0;
    211 	  nl->n_numaux = 0;
    212 	}
    213       else
    214 	{
    215 	  /* Not there.  */
    216 	  nl->n_value = 0;
    217 	  nl->n_scnum = 0;
    218 	  nl->n_type = 0;
    219 	  nl->n_sclass = 0;
    220 	  nl->n_numaux = 0;
    221 	}
    222 
    223       /* Next search request.  */
    224       ++nl;
    225     }
    226 
    227   /* Free the resources.  */
    228   nlist_fshash_fini (table);
    229 
    230   /* We do not need the ELF descriptor anymore.  */
    231   (void) INTUSE(elf_end) (elf);
    232 
    233   /* Neither the file descriptor.  */
    234   (void) close (fd);
    235 
    236   return 0;
    237 
    238  fail_dealloc:
    239   nlist_fshash_fini (table);
    240 
    241  fail_close:
    242   /* We do not need the ELF descriptor anymore.  */
    243   (void) INTUSE(elf_end) (elf);
    244 
    245  fail_fd:
    246   /* Neither the file descriptor.  */
    247   (void) close (fd);
    248 
    249  fail:
    250   /* We have to set all entries to zero.  */
    251   while (nl->n_name != NULL && nl->n_name[0] != '\0')
    252     {
    253       nl->n_value = 0;
    254       nl->n_scnum = 0;
    255       nl->n_type = 0;
    256       nl->n_sclass = 0;
    257       nl->n_numaux = 0;
    258 
    259       /* Next entry.  */
    260       ++nl;
    261     }
    262 
    263   return -1;
    264 }
    265