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
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
nlist(const char * filename,struct nlist * nl)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