blob: bcda71b0d345c0fcf5206ae91b072921e8b61297 [file] [log] [blame]
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00001/* Get frame descriptions. GCC version using .eh_frame.
2 Copyright (C) 2000, 2001, 2002 Red Hat, Inc.
3 Written by Ulrich Drepper <drepper@redhat.com>, 2000.
4
5 This program is Open Source software; you can redistribute it and/or
6 modify it under the terms of the Open Software License version 1.0 as
7 published by the Open Source Initiative.
8
9 You should have received a copy of the Open Software License along
10 with this program; if not, you may obtain a copy of the Open Software
11 License version 1.0 from http://www.opensource.org/licenses/osl.php or
12 by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
13 3001 King Ranch Road, Ukiah, CA 95482. */
14
15#ifdef HAVE_CONFIG_H
16# include <config.h>
17#endif
18
19#include <stdlib.h>
20#include <string.h>
21
22#include <libdwarfP.h>
23
24
25struct cielist
26{
27 Dwarf_Cie cie;
28 struct cielist *next;
29};
30
31
32struct fdelist
33{
34 Dwarf_Fde fde;
35 Dwarf_Small *cie_id_ptr;
36 struct fdelist *next;
37};
38
39
40int
41dwarf_get_fde_list_eh (dbg, cie_data, cie_element_count, fde_data,
42 fde_element_count, error)
43 Dwarf_Debug dbg;
44 Dwarf_Cie **cie_data;
45 Dwarf_Signed *cie_element_count;
46 Dwarf_Fde **fde_data;
47 Dwarf_Signed *fde_element_count;
48 Dwarf_Error *error;
49{
50 Dwarf_Small *readp;
51 Dwarf_Small *readendp;
52 struct cielist *cielist = NULL;
53 struct cielist *copy_cielist;
54 unsigned int ncielist = 0;
55 struct fdelist *fdelist = NULL;
56 unsigned int nfdelist = 0;
57
58 if (dbg->sections[IDX_eh_frame].addr == NULL)
59 return DW_DLV_NO_ENTRY;
60
61 readp = (Dwarf_Small *) dbg->sections[IDX_eh_frame].addr;
62 readendp = readp + dbg->sections[IDX_eh_frame].size;
63
64 while (readp < readendp)
65 {
66 /* Each CIE contains the following:
67
68 1. CIE_length (initial length)
69
70 A constant that gives the number of bytes of the CIE
71 structure, not including the length field, itself [...].
72
73 2. CIE_id
74
75 A constant that is used to distinguish CIEs from FDEs.
76
77 3. version (ubyte) [...]
78
79 4. augmentation (array of ubyte)
80
81 A null-terminated string that identifies the augmentation to
82 this CIE or to the FDEs that use it.
83
84 5. code_alignment_factor (unsigned LEB128)
85
86 A constant that is factored out of all advance location
87 instructions (see below).
88
89 6. data_alignment_factor (signed LEB128)
90
91 A constant that is factored out of all offset instructions
92 [...].
93
94 7. return_address_register (ubyte)
95
96 A constant that indicates which column in the rule table
97 represents the return address of the function.
98
99 8. initial_instructions (array of ubyte) [...] */
100 Dwarf_Small *fde_cie_start;
101 Dwarf_Small *readstartp;
102 Dwarf_Small *cie_id_ptr;
103 Dwarf_Unsigned length;
104 unsigned int address_size;
105 Dwarf_Unsigned start_offset;
106 Dwarf_Unsigned cie_id;
107
108 /* Remember where this entry started. */
109 fde_cie_start = readp;
110 start_offset = (readp
111 - (Dwarf_Small *) dbg->sections[IDX_eh_frame].addr);
112
113 length = read_4ubyte_unaligned (dbg, readp);
114 address_size = 4;
115 readp += 4;
116 if (length == 0xffffffff)
117 {
118 length = read_8ubyte_unaligned (dbg, readp);
119 readp += 8;
120 address_size = 8;
121 }
122 readstartp = readp;
123
124 /* Requirement from the DWARF specification. */
125 if (unlikely (length % address_size != 0))
126 {
127 /* XXX Free resources. */
128 __libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
129 return DW_DLV_ERROR;
130 }
131
132 /* No more entries. */
133 if (length == 0)
134 break;
135
136 cie_id_ptr = readp;
137 if (address_size == 4)
138 {
139 cie_id = read_4sbyte_unaligned (dbg, readp);
140 readp += 4;
141 }
142 else
143 {
144 cie_id = read_8sbyte_unaligned (dbg, readp);
145 readp += 8;
146 }
147
148 /* Now we can distinguish between CIEs and FDEs. gcc uses 0 to
149 signal the record is a CIE. */
150 if (cie_id == 0)
151 {
152 char *augmentation;
153 Dwarf_Unsigned code_alignment_factor;
154 Dwarf_Signed data_alignment_factor;
155 Dwarf_Small *initial_instructions;
156 Dwarf_Small return_address_register;
157 struct cielist *new_cie;
158
159 if (unlikely (*readp++ != CIE_VERSION))
160 {
161 __libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
162 return DW_DLV_ERROR;
163 }
164
165 augmentation = (char *) readp;
166 readp += strlen (augmentation) + 1;
167
168 if (strcmp (augmentation, "") == 0)
169 {
170 get_uleb128 (code_alignment_factor, readp);
171 get_sleb128 (data_alignment_factor, readp);
172 return_address_register = *readp++;
173 initial_instructions = readp;
174 }
175 else if (strcmp (augmentation, "eh") == 0)
176 {
177 /* GCC exception handling. It has an extra field next
178 which is the address of a exception table. We ignore
179 this value since it's only used at runtime by the
180 exception handling. */
181 readp += address_size;
182
183 /* Now the standard fields. */
184 get_uleb128 (code_alignment_factor, readp);
185 get_sleb128 (data_alignment_factor, readp);
186 return_address_register = *readp++;
187 initial_instructions = readp;
188 }
189 else
190 {
191 /* We don't know this augmentation. Skip the rest. The
192 specification says that nothing after the augmentation
193 string is usable. */
194 code_alignment_factor = 0;
195 data_alignment_factor = 0;
196 return_address_register = 0;
197 initial_instructions = NULL;
198 }
199
200 /* Go to the next record. */
201 readp = readstartp + length;
202
203 /* Create the new CIE record. */
204 new_cie = (struct cielist *) alloca (sizeof (struct cielist));
205 new_cie->cie = (Dwarf_Cie) malloc (sizeof (struct Dwarf_Cie_s));
206 if (new_cie->cie == NULL)
207 {
208 __libdwarf_error (dbg, error, DW_E_NOMEM);
209 return DW_DLV_ERROR;
210 }
211
212 new_cie->cie->dbg = dbg;
213 new_cie->cie->length = length;
214 new_cie->cie->augmentation = augmentation;
215 new_cie->cie->code_alignment_factor = code_alignment_factor;
216 new_cie->cie->data_alignment_factor = data_alignment_factor;
217 new_cie->cie->return_address_register = return_address_register;
218 new_cie->cie->initial_instructions = initial_instructions;
219 new_cie->cie->initial_instructions_length =
220 readp - initial_instructions;
221
222 new_cie->cie->offset = start_offset;
223 new_cie->cie->index = ncielist;
224 new_cie->next = cielist;
225 cielist = new_cie;
226 ++ncielist;
227 }
228 else
229 {
230 Dwarf_Addr initial_location;
231 Dwarf_Unsigned address_range;
232 Dwarf_Small *instructions;
233 struct fdelist *new_fde;
234 struct cielist *cie;
235
236 if (address_size == 4)
237 {
238 initial_location = read_4ubyte_unaligned (dbg, readp);
239 readp += 4;
240 address_range = read_4ubyte_unaligned (dbg, readp);
241 readp += 4;
242 }
243 else
244 {
245 initial_location = read_8ubyte_unaligned (dbg, readp);
246 readp += 8;
247 address_range = read_8ubyte_unaligned (dbg, readp);
248 readp += 8;
249 }
250
251 instructions = readp;
252
253 /* Go to the next record. */
254 readp = readstartp + length;
255
256 /* Create the new FDE record. */
257 new_fde = (struct fdelist *) alloca (sizeof (struct fdelist));
258 new_fde->fde = (Dwarf_Fde) malloc (sizeof (struct Dwarf_Fde_s));
259 if (new_fde->fde == NULL)
260 {
261 __libdwarf_error (dbg, error, DW_E_NOMEM);
262 return DW_DLV_ERROR;
263 }
264
265 new_fde->fde->initial_location = initial_location;
266 new_fde->fde->address_range = address_range;
267 new_fde->fde->instructions = instructions;
268 new_fde->fde->instructions_length = readp - instructions;
269 new_fde->fde->fde_bytes = fde_cie_start;
270 new_fde->fde->fde_byte_length = readstartp + length - fde_cie_start;
271 new_fde->fde->cie = NULL;
272 new_fde->cie_id_ptr = cie_id_ptr;
273
274 for (cie = cielist; cie != NULL; cie = cie->next)
275 /* This test takes the non-standard way of using the CIE ID
276 in the GNU .eh_frame sectio into account. Instead of being
277 a direct offset in the section it is a offset from the
278 location of the FDE'S CIE ID value itself to the CIE entry. */
279 if (cie->cie->offset
280 == (size_t) (cie_id_ptr - cie_id
281 - (Dwarf_Small *) dbg->sections[IDX_eh_frame].addr))
282 {
283 new_fde->fde->cie = cie->cie;
284 break;
285 }
286
287 new_fde->fde->offset = cie_id;
288 new_fde->next = fdelist;
289 fdelist = new_fde;
290 ++nfdelist;
291 }
292 }
293
294 /* There must always be at least one CIE. */
295 if (unlikely (ncielist == 0))
296 {
297 __libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
298 return DW_DLV_ERROR;
299 }
300
301 /* Create the lists. */
302 *cie_data = (Dwarf_Cie *) malloc (ncielist * sizeof (struct Dwarf_Cie_s));
303 if (nfdelist > 0)
304 *fde_data = (Dwarf_Fde *) malloc (nfdelist * sizeof (struct Dwarf_Fde_s));
305 else
306 *fde_data = NULL;
307 if ((nfdelist > 0 && *fde_data == NULL) || *cie_data == NULL)
308 {
309 __libdwarf_error (dbg, error, DW_E_NOMEM);
310 return DW_DLV_ERROR;
311 }
312
313 /* Remember the counts. */
314 dbg->fde_cnt = nfdelist;
315 dbg->cie_cnt = ncielist;
316
317 /* Add all the CIEs. */
318 copy_cielist = cielist;
319 *cie_element_count = ncielist;
320 while (ncielist-- > 0)
321 {
322 (*cie_data)[ncielist] = cielist->cie;
323 cielist = cielist->next;
324 }
325
326 /* Add all the FDEs. */
327 *fde_element_count = nfdelist;
328 while (nfdelist-- > 0)
329 {
330 (*fde_data)[nfdelist] = fdelist->fde;
331
332 if (fdelist->fde->cie == NULL)
333 {
334 /* We have not yet found the CIE. Search now that we know
335 about all of them. */
336 cielist = copy_cielist;
337 do
338 {
339 if (cielist->cie->offset
340 == (size_t) (fdelist->cie_id_ptr - fdelist->fde->offset
341 - (Dwarf_Small *) dbg->sections[IDX_eh_frame].addr))
342 {
343 fdelist->fde->cie = cielist->cie;
344 break;
345 }
346 cielist = cielist->next;
347 }
348 while (cielist != NULL);
349
350 if (cielist == NULL)
351 {
352 /* There is no matching CIE. This is bad. */
353 /* XXX Free everything. */
354 __libdwarf_error (dbg, error, DW_E_INVALID_DWARF);
355 return DW_DLV_ERROR;
356 }
357 }
358
359 fdelist = fdelist->next;
360 }
361
362 return DW_DLV_OK;
363}