Imported Upstream version 0.155
[platform/upstream/elfutils.git] / libdw / dwarf_getpubnames.c
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
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       if (mem[cnt].set_start >= dbg->sectiondata[IDX_debug_pubnames]->d_size)
92         /* Something wrong, the first entry is beyond the end of
93            the section.  */
94         break;
95
96       /* Read the version.  It better be two for now.  */
97       uint16_t version = read_2ubyte_unaligned (dbg, readp);
98       if (unlikely (version != 2))
99         {
100           __libdw_seterrno (DWARF_E_INVALID_VERSION);
101           goto err_return;
102         }
103
104       /* Get the CU offset.  */
105       if (__libdw_read_offset (dbg, dbg, IDX_debug_pubnames,
106                                readp + 2, len_bytes,
107                                &mem[cnt].cu_offset, IDX_debug_info, 3))
108         /* Error has been already set in reader.  */
109         goto err_return;
110
111       /* Determine the size of the CU header.  */
112       unsigned char *infop
113         = ((unsigned char *) dbg->sectiondata[IDX_debug_info]->d_buf
114            + mem[cnt].cu_offset);
115       if (read_4ubyte_unaligned_noncvt (infop) == DWARF3_LENGTH_64_BIT)
116         mem[cnt].cu_header_size = 23;
117       else
118         mem[cnt].cu_header_size = 11;
119
120       ++cnt;
121
122       /* Advance to the next set.  */
123       readp += len;
124     }
125
126   if (mem == NULL)
127     {
128       __libdw_seterrno (DWARF_E_NO_ENTRY);
129       return -1;
130     }
131
132   dbg->pubnames_sets = (struct pubnames_s *) realloc (mem, cnt * entsize);
133   dbg->pubnames_nsets = cnt;
134
135   return 0;
136 }
137
138
139 ptrdiff_t
140 dwarf_getpubnames (dbg, callback, arg, offset)
141      Dwarf *dbg;
142      int (*callback) (Dwarf *, Dwarf_Global *, void *);
143      void *arg;
144      ptrdiff_t offset;
145 {
146   if (dbg == NULL)
147     return -1l;
148
149   if (unlikely (offset < 0))
150     {
151       __libdw_seterrno (DWARF_E_INVALID_OFFSET);
152       return -1l;
153     }
154
155   /* Make sure it is a valid offset.  */
156   if (unlikely (dbg->sectiondata[IDX_debug_pubnames] == NULL
157                 || ((size_t) offset
158                     >= dbg->sectiondata[IDX_debug_pubnames]->d_size)))
159     /* No (more) entry.  */
160     return 0;
161
162   /* If necessary read the set information.  */
163   if (dbg->pubnames_nsets == 0 && unlikely (get_offsets (dbg) != 0))
164     return -1l;
165
166   /* Find the place where to start.  */
167   size_t cnt;
168   if (offset == 0)
169     {
170       cnt = 0;
171       offset = dbg->pubnames_sets[0].set_start;
172     }
173   else
174     {
175       for (cnt = 0; cnt + 1 < dbg->pubnames_nsets; ++cnt)
176         if ((Dwarf_Off) offset >= dbg->pubnames_sets[cnt].set_start)
177           {
178             assert ((Dwarf_Off) offset
179                     < dbg->pubnames_sets[cnt + 1].set_start);
180             break;
181           }
182       assert (cnt + 1 < dbg->pubnames_nsets);
183     }
184
185   unsigned char *startp
186     = (unsigned char *) dbg->sectiondata[IDX_debug_pubnames]->d_buf;
187   unsigned char *readp = startp + offset;
188   while (1)
189     {
190       Dwarf_Global gl;
191
192       gl.cu_offset = (dbg->pubnames_sets[cnt].cu_offset
193                       + dbg->pubnames_sets[cnt].cu_header_size);
194
195       while (1)
196         {
197           /* READP points to the next offset/name pair.  */
198           if (dbg->pubnames_sets[cnt].address_len == 4)
199             gl.die_offset = read_4ubyte_unaligned_inc (dbg, readp);
200           else
201             gl.die_offset = read_8ubyte_unaligned_inc (dbg, readp);
202
203           /* If the offset is zero we reached the end of the set.  */
204           if (gl.die_offset == 0)
205             break;
206
207           /* Add the CU offset.  */
208           gl.die_offset += dbg->pubnames_sets[cnt].cu_offset;
209
210           gl.name = (char *) readp;
211           readp = (unsigned char *) rawmemchr (gl.name, '\0') + 1;
212
213           /* We found name and DIE offset.  Report it.  */
214           if (callback (dbg, &gl, arg) != DWARF_CB_OK)
215             {
216               /* The user wants us to stop.  Return the offset of the
217                  next entry.  */
218               return readp - startp;
219             }
220         }
221
222       if (++cnt == dbg->pubnames_nsets)
223         /* This was the last set.  */
224         break;
225
226       startp = (unsigned char *) dbg->sectiondata[IDX_debug_pubnames]->d_buf;
227       readp = startp + dbg->pubnames_sets[cnt].set_start;
228     }
229
230   /* We are done.  No more entries.  */
231   return 0;
232 }