1 /* Get public symbol information.
2 Copyright (C) 2002, 2003, 2004, 2005, 2008 Red Hat, Inc.
3 This file is part of elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2002.
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 <assert.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/param.h>
38
39 #include <libdwP.h>
40 #include <dwarf.h>
41
42
43 static int
get_offsets(Dwarf * dbg)44 get_offsets (Dwarf *dbg)
45 {
46 size_t allocated = 0;
47 size_t cnt = 0;
48 struct pubnames_s *mem = NULL;
49 const size_t entsize = sizeof (struct pubnames_s);
50 unsigned char *const startp = dbg->sectiondata[IDX_debug_pubnames]->d_buf;
51 unsigned char *readp = startp;
52 unsigned char *endp = readp + dbg->sectiondata[IDX_debug_pubnames]->d_size;
53
54 while (readp + 14 < endp)
55 {
56 /* If necessary, allocate more entries. */
57 if (cnt >= allocated)
58 {
59 allocated = MAX (10, 2 * allocated);
60 struct pubnames_s *newmem
61 = (struct pubnames_s *) realloc (mem, allocated * entsize);
62 if (newmem == NULL)
63 {
64 __libdw_seterrno (DWARF_E_NOMEM);
65 err_return:
66 free (mem);
67 return -1;
68 }
69
70 mem = newmem;
71 }
72
73 /* Read the set header. */
74 int len_bytes = 4;
75 Dwarf_Off len = read_4ubyte_unaligned_inc (dbg, readp);
76 if (len == DWARF3_LENGTH_64_BIT)
77 {
78 len = read_8ubyte_unaligned_inc (dbg, readp);
79 len_bytes = 8;
80 }
81 else if (unlikely (len >= DWARF3_LENGTH_MIN_ESCAPE_CODE
82 && len <= DWARF3_LENGTH_MAX_ESCAPE_CODE))
83 {
84 __libdw_seterrno (DWARF_E_INVALID_DWARF);
85 goto err_return;
86 }
87
88 /* Now we know the offset of the first offset/name pair. */
89 mem[cnt].set_start = readp + 2 + 2 * len_bytes - startp;
90 mem[cnt].address_len = len_bytes;
91 size_t max_size = dbg->sectiondata[IDX_debug_pubnames]->d_size;
92 if (mem[cnt].set_start >= max_size
93 || len - (2 + 2 * len_bytes) > max_size - mem[cnt].set_start)
94 /* Something wrong, the first entry is beyond the end of
95 the section. Or the length of the whole unit is too big. */
96 break;
97
98 /* Read the version. It better be two for now. */
99 uint16_t version = read_2ubyte_unaligned (dbg, readp);
100 if (unlikely (version != 2))
101 {
102 __libdw_seterrno (DWARF_E_INVALID_VERSION);
103 goto err_return;
104 }
105
106 /* Get the CU offset. */
107 if (__libdw_read_offset (dbg, dbg, IDX_debug_pubnames,
108 readp + 2, len_bytes,
109 &mem[cnt].cu_offset, IDX_debug_info, 3))
110 /* Error has been already set in reader. */
111 goto err_return;
112
113 /* Determine the size of the CU header. */
114 unsigned char *infop
115 = ((unsigned char *) dbg->sectiondata[IDX_debug_info]->d_buf
116 + mem[cnt].cu_offset);
117 if (read_4ubyte_unaligned_noncvt (infop) == DWARF3_LENGTH_64_BIT)
118 mem[cnt].cu_header_size = 23;
119 else
120 mem[cnt].cu_header_size = 11;
121
122 ++cnt;
123
124 /* Advance to the next set. */
125 readp += len;
126 }
127
128 if (mem == NULL || cnt == 0)
129 {
130 __libdw_seterrno (DWARF_E_NO_ENTRY);
131 return -1;
132 }
133
134 dbg->pubnames_sets = (struct pubnames_s *) realloc (mem, cnt * entsize);
135 dbg->pubnames_nsets = cnt;
136
137 return 0;
138 }
139
140
141 ptrdiff_t
dwarf_getpubnames(dbg,callback,arg,offset)142 dwarf_getpubnames (dbg, callback, arg, offset)
143 Dwarf *dbg;
144 int (*callback) (Dwarf *, Dwarf_Global *, void *);
145 void *arg;
146 ptrdiff_t offset;
147 {
148 if (dbg == NULL)
149 return -1l;
150
151 if (unlikely (offset < 0))
152 {
153 __libdw_seterrno (DWARF_E_INVALID_OFFSET);
154 return -1l;
155 }
156
157 /* Make sure it is a valid offset. */
158 if (unlikely (dbg->sectiondata[IDX_debug_pubnames] == NULL
159 || ((size_t) offset
160 >= dbg->sectiondata[IDX_debug_pubnames]->d_size)))
161 /* No (more) entry. */
162 return 0;
163
164 /* If necessary read the set information. */
165 if (dbg->pubnames_nsets == 0 && unlikely (get_offsets (dbg) != 0))
166 return -1l;
167
168 /* Find the place where to start. */
169 size_t cnt;
170 if (offset == 0)
171 {
172 cnt = 0;
173 offset = dbg->pubnames_sets[0].set_start;
174 }
175 else
176 {
177 for (cnt = 0; cnt + 1 < dbg->pubnames_nsets; ++cnt)
178 if ((Dwarf_Off) offset >= dbg->pubnames_sets[cnt].set_start)
179 {
180 assert ((Dwarf_Off) offset
181 < dbg->pubnames_sets[cnt + 1].set_start);
182 break;
183 }
184 assert (cnt + 1 < dbg->pubnames_nsets);
185 }
186
187 unsigned char *startp
188 = (unsigned char *) dbg->sectiondata[IDX_debug_pubnames]->d_buf;
189 unsigned char *endp
190 = startp + dbg->sectiondata[IDX_debug_pubnames]->d_size;
191 unsigned char *readp = startp + offset;
192 while (1)
193 {
194 Dwarf_Global gl;
195
196 gl.cu_offset = (dbg->pubnames_sets[cnt].cu_offset
197 + dbg->pubnames_sets[cnt].cu_header_size);
198
199 while (1)
200 {
201 /* READP points to the next offset/name pair. */
202 if (readp + dbg->pubnames_sets[cnt].address_len > endp)
203 goto invalid_dwarf;
204 if (dbg->pubnames_sets[cnt].address_len == 4)
205 gl.die_offset = read_4ubyte_unaligned_inc (dbg, readp);
206 else
207 gl.die_offset = read_8ubyte_unaligned_inc (dbg, readp);
208
209 /* If the offset is zero we reached the end of the set. */
210 if (gl.die_offset == 0)
211 break;
212
213 /* Add the CU offset. */
214 gl.die_offset += dbg->pubnames_sets[cnt].cu_offset;
215
216 gl.name = (char *) readp;
217 readp = (unsigned char *) memchr (gl.name, '\0', endp - readp);
218 if (unlikely (readp == NULL))
219 {
220 invalid_dwarf:
221 __libdw_seterrno (DWARF_E_INVALID_DWARF);
222 return -1l;
223 }
224 readp++;
225
226 /* We found name and DIE offset. Report it. */
227 if (callback (dbg, &gl, arg) != DWARF_CB_OK)
228 {
229 /* The user wants us to stop. Return the offset of the
230 next entry. */
231 return readp - startp;
232 }
233 }
234
235 if (++cnt == dbg->pubnames_nsets)
236 /* This was the last set. */
237 break;
238
239 startp = (unsigned char *) dbg->sectiondata[IDX_debug_pubnames]->d_buf;
240 readp = startp + dbg->pubnames_sets[cnt].set_start;
241 }
242
243 /* We are done. No more entries. */
244 return 0;
245 }
246