Imported Upstream version 0.153
[platform/upstream/elfutils.git] / libelf / nlist.c
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@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 }