1/* Extract symbol list from binary.
2   Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
3   Written by Ulrich Drepper <drepper@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
31struct 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
47int
48nlist (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