1/*-
2 * Copyright (c) 2007 John Birrell (jb@freebsd.org)
3 * Copyright (c) 2009,2011,2014 Kai Wang
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include "_libdwarf.h"
29
30ELFTC_VCSID("$Id: dwarf_die.c 3039 2014-05-18 15:10:56Z kaiwang27 $");
31
32int
33dwarf_child(Dwarf_Die die, Dwarf_Die *ret_die, Dwarf_Error *error)
34{
35	Dwarf_Debug dbg;
36	Dwarf_Section *ds;
37	Dwarf_CU cu;
38	int ret;
39
40	dbg = die != NULL ? die->die_dbg : NULL;
41
42	if (die == NULL || ret_die == NULL) {
43		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
44		return (DW_DLV_ERROR);
45	}
46
47	if (die->die_ab->ab_children == DW_CHILDREN_no)
48		return (DW_DLV_NO_ENTRY);
49
50	dbg = die->die_dbg;
51	cu = die->die_cu;
52	ds = cu->cu_is_info ? dbg->dbg_info_sec : dbg->dbg_types_sec;
53	ret = _dwarf_die_parse(die->die_dbg, ds, cu, cu->cu_dwarf_size,
54	    die->die_next_off, cu->cu_next_offset, ret_die, 0, error);
55
56	if (ret == DW_DLE_NO_ENTRY) {
57		DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
58		return (DW_DLV_NO_ENTRY);
59	} else if (ret != DW_DLE_NONE)
60		return (DW_DLV_ERROR);
61
62	return (DW_DLV_OK);
63}
64
65int
66dwarf_siblingof_b(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Die *ret_die,
67    Dwarf_Bool is_info, Dwarf_Error *error)
68{
69	Dwarf_CU cu;
70	Dwarf_Attribute at;
71	Dwarf_Section *ds;
72	uint64_t offset;
73	int ret, search_sibling;
74
75	if (dbg == NULL || ret_die == NULL) {
76		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
77		return (DW_DLV_ERROR);
78	}
79
80	ds = is_info ? dbg->dbg_info_sec : dbg->dbg_types_sec;
81	cu = is_info ? dbg->dbg_cu_current : dbg->dbg_tu_current;
82
83	if (cu == NULL) {
84		DWARF_SET_ERROR(dbg, error, DW_DLE_DIE_NO_CU_CONTEXT);
85		return (DW_DLV_ERROR);
86	}
87
88	/* Application requests the first DIE in this CU. */
89	if (die == NULL)
90		return (dwarf_offdie_b(dbg, cu->cu_1st_offset, is_info,
91		    ret_die, error));
92
93	/*
94	 * Check if the `is_info' flag matches the debug section the
95	 * DIE belongs to.
96	 */
97	if (is_info != die->die_cu->cu_is_info) {
98		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
99		return (DW_DLV_ERROR);
100	}
101
102	/*
103	 * If the DIE doesn't have any children, its sibling sits next
104	 * right to it.
105	 */
106	search_sibling = 0;
107	if (die->die_ab->ab_children == DW_CHILDREN_no)
108		offset = die->die_next_off;
109	else {
110		/*
111		 * Look for DW_AT_sibling attribute for the offset of
112		 * its sibling.
113		 */
114		if ((at = _dwarf_attr_find(die, DW_AT_sibling)) != NULL) {
115			if (at->at_form != DW_FORM_ref_addr)
116				offset = at->u[0].u64 + cu->cu_offset;
117			else
118				offset = at->u[0].u64;
119		} else {
120			offset = die->die_next_off;
121			search_sibling = 1;
122		}
123	}
124
125	ret = _dwarf_die_parse(die->die_dbg, ds, cu, cu->cu_dwarf_size, offset,
126	    cu->cu_next_offset, ret_die, search_sibling, error);
127
128	if (ret == DW_DLE_NO_ENTRY) {
129		DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
130		return (DW_DLV_NO_ENTRY);
131	} else if (ret != DW_DLE_NONE)
132		return (DW_DLV_ERROR);
133
134	return (DW_DLV_OK);
135}
136
137
138int
139dwarf_siblingof(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Die *ret_die,
140    Dwarf_Error *error)
141{
142
143	return (dwarf_siblingof_b(dbg, die, ret_die, 1, error));
144}
145
146static int
147_dwarf_search_die_within_cu(Dwarf_Debug dbg, Dwarf_Section *s, Dwarf_CU cu,
148    Dwarf_Off offset, Dwarf_Die *ret_die, Dwarf_Error *error)
149{
150
151	assert(dbg != NULL && cu != NULL && ret_die != NULL);
152
153	return (_dwarf_die_parse(dbg, s, cu, cu->cu_dwarf_size,
154	    offset, cu->cu_next_offset, ret_die, 0, error));
155}
156
157int
158dwarf_offdie_b(Dwarf_Debug dbg, Dwarf_Off offset, Dwarf_Bool is_info,
159    Dwarf_Die *ret_die, Dwarf_Error *error)
160{
161	Dwarf_Section *ds;
162	Dwarf_CU cu;
163	int ret;
164
165	if (dbg == NULL || ret_die == NULL) {
166		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
167		return (DW_DLV_ERROR);
168	}
169
170	ds = is_info ? dbg->dbg_info_sec : dbg->dbg_types_sec;
171	cu = is_info ? dbg->dbg_cu_current : dbg->dbg_tu_current;
172
173	/* First search the current CU. */
174	if (cu != NULL) {
175		if (offset > cu->cu_offset && offset < cu->cu_next_offset) {
176			ret = _dwarf_search_die_within_cu(dbg, ds, cu, offset,
177			    ret_die, error);
178			if (ret == DW_DLE_NO_ENTRY) {
179				DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
180				return (DW_DLV_NO_ENTRY);
181			} else if (ret != DW_DLE_NONE)
182				return (DW_DLV_ERROR);
183			return (DW_DLV_OK);
184		}
185	}
186
187	/* Search other CUs. */
188	ret = _dwarf_info_load(dbg, 1, is_info, error);
189	if (ret != DW_DLE_NONE)
190		return (DW_DLV_ERROR);
191
192	if (is_info) {
193		STAILQ_FOREACH(cu, &dbg->dbg_cu, cu_next) {
194			if (offset < cu->cu_offset ||
195			    offset > cu->cu_next_offset)
196				continue;
197			ret = _dwarf_search_die_within_cu(dbg, ds, cu, offset,
198			    ret_die, error);
199			if (ret == DW_DLE_NO_ENTRY) {
200				DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
201				return (DW_DLV_NO_ENTRY);
202			} else if (ret != DW_DLE_NONE)
203				return (DW_DLV_ERROR);
204			return (DW_DLV_OK);
205		}
206	} else {
207		STAILQ_FOREACH(cu, &dbg->dbg_tu, cu_next) {
208			if (offset < cu->cu_offset ||
209			    offset > cu->cu_next_offset)
210				continue;
211			ret = _dwarf_search_die_within_cu(dbg, ds, cu, offset,
212			    ret_die, error);
213			if (ret == DW_DLE_NO_ENTRY) {
214				DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
215				return (DW_DLV_NO_ENTRY);
216			} else if (ret != DW_DLE_NONE)
217				return (DW_DLV_ERROR);
218			return (DW_DLV_OK);
219		}
220	}
221
222	DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
223	return (DW_DLV_NO_ENTRY);
224}
225
226int
227dwarf_offdie(Dwarf_Debug dbg, Dwarf_Off offset, Dwarf_Die *ret_die,
228    Dwarf_Error *error)
229{
230
231	return (dwarf_offdie_b(dbg, offset, 1, ret_die, error));
232}
233
234int
235dwarf_tag(Dwarf_Die die, Dwarf_Half *tag, Dwarf_Error *error)
236{
237	Dwarf_Debug dbg;
238
239	dbg = die != NULL ? die->die_dbg : NULL;
240
241	if (die == NULL || tag == NULL) {
242		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
243		return (DW_DLV_ERROR);
244	}
245
246	assert(die->die_ab != NULL);
247
248	*tag = (Dwarf_Half) die->die_ab->ab_tag;
249
250	return (DW_DLV_OK);
251}
252
253int
254dwarf_dieoffset(Dwarf_Die die, Dwarf_Off *ret_offset, Dwarf_Error *error)
255{
256	Dwarf_Debug dbg;
257
258	dbg = die != NULL ? die->die_dbg : NULL;
259
260	if (die == NULL || ret_offset == NULL) {
261		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
262		return (DW_DLV_ERROR);
263	}
264
265	*ret_offset = die->die_offset;
266
267	return (DW_DLV_OK);
268}
269
270int
271dwarf_die_CU_offset(Dwarf_Die die, Dwarf_Off *ret_offset, Dwarf_Error *error)
272{
273	Dwarf_Debug dbg;
274	Dwarf_CU cu;
275
276	dbg = die != NULL ? die->die_dbg : NULL;
277
278	if (die == NULL || ret_offset == NULL) {
279		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
280		return (DW_DLV_ERROR);
281	}
282
283	cu = die->die_cu;
284	assert(cu != NULL);
285
286	*ret_offset = die->die_offset - cu->cu_offset;
287
288	return (DW_DLV_OK);
289}
290
291int
292dwarf_die_CU_offset_range(Dwarf_Die die, Dwarf_Off *cu_offset,
293    Dwarf_Off *cu_length, Dwarf_Error *error)
294{
295	Dwarf_Debug dbg;
296	Dwarf_CU cu;
297
298	dbg = die != NULL ? die->die_dbg : NULL;
299
300	if (die == NULL || cu_offset == NULL || cu_length == NULL) {
301		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
302		return (DW_DLV_ERROR);
303	}
304
305	cu = die->die_cu;
306	assert(cu != NULL);
307
308	*cu_offset = cu->cu_offset;
309	*cu_length = cu->cu_length + cu->cu_length_size;
310
311	return (DW_DLV_OK);
312}
313
314int
315dwarf_diename(Dwarf_Die die, char **ret_name, Dwarf_Error *error)
316{
317	Dwarf_Debug dbg;
318
319	dbg = die != NULL ? die->die_dbg : NULL;
320
321	if (die == NULL || ret_name == NULL) {
322		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
323		return (DW_DLV_ERROR);
324	}
325
326	if (die->die_name == NULL) {
327		DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
328		return (DW_DLV_NO_ENTRY);
329	}
330
331	*ret_name = die->die_name;
332
333	return (DW_DLV_OK);
334}
335
336int
337dwarf_die_abbrev_code(Dwarf_Die die)
338{
339
340	assert(die != NULL);
341
342	return (die->die_abnum);
343}
344
345int
346dwarf_get_cu_die_offset_given_cu_header_offset_b(Dwarf_Debug dbg,
347    Dwarf_Off in_cu_header_offset, Dwarf_Bool is_info,
348    Dwarf_Off *out_cu_die_offset, Dwarf_Error *error)
349{
350	Dwarf_CU cu;
351
352	if (dbg == NULL || out_cu_die_offset == NULL) {
353		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
354		return (DW_DLV_ERROR);
355	}
356
357	if (is_info) {
358		STAILQ_FOREACH(cu, &dbg->dbg_cu, cu_next) {
359			if (cu->cu_offset == in_cu_header_offset) {
360				*out_cu_die_offset = cu->cu_1st_offset;
361				break;
362			}
363		}
364	} else {
365		STAILQ_FOREACH(cu, &dbg->dbg_tu, cu_next) {
366			if (cu->cu_offset == in_cu_header_offset) {
367				*out_cu_die_offset = cu->cu_1st_offset;
368				break;
369			}
370		}
371	}
372
373	if (cu == NULL) {
374		DWARF_SET_ERROR(dbg, error, DW_DLE_NO_ENTRY);
375		return (DW_DLV_NO_ENTRY);
376	}
377
378	return (DW_DLV_OK);
379}
380
381int
382dwarf_get_cu_die_offset_given_cu_header_offset(Dwarf_Debug dbg,
383    Dwarf_Off in_cu_header_offset, Dwarf_Off *out_cu_die_offset,
384    Dwarf_Error *error)
385{
386
387	return (dwarf_get_cu_die_offset_given_cu_header_offset_b(dbg,
388	    in_cu_header_offset, 1, out_cu_die_offset, error));
389}
390
391int
392dwarf_get_address_size(Dwarf_Debug dbg, Dwarf_Half *addr_size,
393    Dwarf_Error *error)
394{
395
396	if (dbg == NULL || addr_size == NULL) {
397		DWARF_SET_ERROR(dbg, error, DW_DLE_ARGUMENT);
398		return (DW_DLV_ERROR);
399	}
400
401	*addr_size = dbg->dbg_pointer_size;
402
403	return (DW_DLV_OK);
404}
405
406Dwarf_Bool
407dwarf_get_die_infotypes_flag(Dwarf_Die die)
408{
409
410	assert(die != NULL);
411
412	return (die->die_cu->cu_is_info);
413}
414