1/*-
2 * Copyright (c) 2008-2009, 2011, Juniper Networks, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 *	JNPR: dwarf_func.c 336441 2009-10-17 09:19:54Z deo
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <stdlib.h>
33#include <string.h>
34#include <libdwarf.h>
35#include <_libdwarf.h>
36
37static void
38dwarf_add_function(Dwarf_Debug dbg, Dwarf_Func func)
39{
40
41	STAILQ_INSERT_TAIL(&dbg->dbg_func, func, func_next);
42}
43
44int
45dwarf_function_get_addr_range(Dwarf_Func f, Dwarf_Addr *low_pc,
46    Dwarf_Addr *high_pc)
47{
48
49	*low_pc = f->func_low_pc;
50	*high_pc = f->func_high_pc;
51	return 0;
52}
53
54int
55dwarf_inlined_function_get_addr_range(Dwarf_Inlined_Func f, Dwarf_Addr *low_pc,
56    Dwarf_Addr *high_pc)
57{
58
59	*low_pc = f->ifunc_low_pc;
60	*high_pc = f->ifunc_high_pc;
61	return 0;
62}
63
64int
65dwarf_function_is_inlined(Dwarf_Func f)
66{
67
68	if (f->func_is_inlined == DW_INL_inlined ||
69	    f->func_is_inlined == DW_INL_declared_inlined)
70		return 1;
71	else
72		return 0;
73}
74
75Dwarf_Func
76dwarf_find_function_by_name(Dwarf_Debug dbg, const char *name)
77{
78	/* XXX: replace with a fast version */
79
80	Dwarf_Func func;
81	STAILQ_FOREACH(func, &dbg->dbg_func, func_next) {
82		if (strcmp(name, func->func_name) == 0)
83			return func;
84	}
85	return NULL;
86}
87
88Dwarf_Func
89dwarf_find_function_by_offset(Dwarf_Debug dbg, Dwarf_Off off)
90{
91
92	Dwarf_Func func;
93	Dwarf_Die die;
94	/* printf("look for %llx\n", off); */
95	STAILQ_FOREACH(func, &dbg->dbg_func, func_next) {
96		die = func->func_die;
97		if ((off_t)die->die_offset == off) {
98			return func;
99		}
100	}
101	return NULL;
102}
103
104void
105dwarf_build_function_table(Dwarf_Debug dbg)
106{
107	Dwarf_CU cu;
108	Dwarf_AttrValue av;
109	Dwarf_Die die, origin_die;
110	Dwarf_Func func, origin_func;
111	Dwarf_Inlined_Func ifunc;
112	unsigned long long offset;
113	const char *name;
114	Dwarf_Error error;
115
116	/*
117	 * find out all the functions
118	 */
119	STAILQ_FOREACH(cu, &dbg->dbg_cu, cu_next) {
120		STAILQ_FOREACH(die, &cu->cu_die, die_next) {
121			if (die->die_a->a_tag == DW_TAG_subprogram) {
122				/*
123				 * Some function has multiple entries, i.e.
124				 * if a function is inlined, it has many
125				 * abstract/concrete instances, the abstract
126				 * instances are with DW_TAG_subprogram.
127				 */
128				dwarf_attrval_string(die, DW_AT_name, &name,
129				    &error);
130				func = dwarf_find_function_by_name(dbg, name);
131				if (func == NULL) {
132					func = malloc(
133					    sizeof(struct _Dwarf_Func));
134					DWARF_ASSERT(func);
135
136					func->func_die = die;
137					func->func_name = name;
138					STAILQ_INIT(
139					    &func->func_inlined_instances);
140
141					dwarf_add_function(dbg, func);
142					STAILQ_FOREACH(av, &die->die_attrval,
143					    av_next) {
144						switch (av->av_attrib) {
145						case DW_AT_low_pc:
146							func->func_low_pc =
147							    av->u[0].u64;
148							break;
149						case DW_AT_high_pc:
150							func->func_high_pc =
151							    av->u[0].u64;
152							break;
153						case DW_AT_inline:
154							func->func_is_inlined =
155							    av->u[0].u64;
156							break;
157						}
158					}
159				}
160			}
161		}
162	}
163
164	/*
165	 * Now check the concrete inlined instances.
166	 */
167	STAILQ_FOREACH(cu, &dbg->dbg_cu, cu_next) {
168		STAILQ_FOREACH(die, &cu->cu_die, die_next) {
169			if (die->die_a->a_tag == DW_TAG_inlined_subroutine) {
170				ifunc = malloc(
171				    sizeof(struct _Dwarf_Inlined_Func));
172				DWARF_ASSERT(ifunc);
173				STAILQ_FOREACH(av, &die->die_attrval, av_next) {
174					switch (av->av_attrib) {
175					case DW_AT_abstract_origin:
176						offset = av->u[0].u64 +
177						    die->die_cu->cu_offset;
178						origin_die = dwarf_die_find(
179							die, offset);
180						DWARF_ASSERT(origin_die != 0);
181
182						/*
183						 * the abstract origin must
184						 * have been merged with
185						 * another die
186						 */
187						dwarf_attrval_string(
188						    origin_die, DW_AT_name,
189						    &name, &error);
190						origin_func =
191						    dwarf_find_function_by_name
192						    (dbg, name);
193						DWARF_ASSERT(origin_func != 0);
194
195						STAILQ_INSERT_TAIL(
196						    &origin_func->
197						    func_inlined_instances,
198						    ifunc, ifunc_next);
199
200						break;
201					case DW_AT_low_pc:
202						ifunc->ifunc_low_pc =
203						    av->u[0].u64;
204						break;
205					case DW_AT_high_pc:
206						ifunc->ifunc_high_pc =
207						    av->u[0].u64;
208						break;
209					}
210				}
211			}
212		}
213	}
214}
215
216void
217dwarf_function_iterate_inlined_instance(Dwarf_Func func,
218    Dwarf_Inlined_Callback f, void *data)
219{
220	Dwarf_Inlined_Func ifunc;
221
222	if (!dwarf_function_is_inlined(func))
223		return;
224	STAILQ_FOREACH(ifunc, &func->func_inlined_instances, ifunc_next) {
225		f(ifunc, data);
226	}
227}
228