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