1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <stddef.h>
30#include <stdlib.h>
31#include <string.h>
32
33#include "dis_target.h"
34#include "dis_list.h"
35#include "dis_util.h"
36
37/*
38 * List support functions.
39 *
40 * Support routines for managing lists of sections and functions.  We first
41 * process the command line arguments into lists of strings.  For each target,
42 * we resolve these strings against the set of available sections and/or
43 * functions to arrive at the set of objects to disassemble.
44 *
45 * We export two types of lists, namelists and resolvelists.  The first is used
46 * to record names given as command line options.  The latter is used to
47 * maintain the data objects specific to a given target.
48 */
49
50typedef struct unresolved_name {
51	const char	*un_name;	/* name of function or object */
52	int		un_value;	/* user-supplied data */
53	int		un_mark;	/* internal counter */
54	uu_list_node_t	un_node;	/* uulist node */
55} unresolved_name_t;
56
57typedef struct resolved_name {
58	void		*rn_data;	/* section or function data */
59	int		rn_value;	/* user-supplied data */
60	uu_list_node_t	rn_node;	/* uulist node */
61} resolved_name_t;
62
63static uu_list_pool_t *unresolved_pool;
64static uu_list_pool_t *resolved_pool;
65static int current_mark = 0;
66
67static void
68initialize_pools(void)
69{
70	unresolved_pool = uu_list_pool_create(
71	    "unresolved_pool", sizeof (unresolved_name_t),
72	    offsetof(unresolved_name_t, un_node), NULL, 0);
73	resolved_pool = uu_list_pool_create(
74	    "resolved_pool", sizeof (resolved_name_t),
75	    offsetof(resolved_name_t, rn_node), NULL, 0);
76
77	if (unresolved_pool == NULL ||
78	    resolved_pool == NULL)
79		die("out of memory");
80}
81
82/*
83 * Returns an empty list of unresolved names.
84 */
85dis_namelist_t *
86dis_namelist_create(void)
87{
88	uu_list_t *listp;
89
90	/*
91	 * If this is the first request to create a list, initialize the list
92	 * pools.
93	 */
94	if (unresolved_pool == NULL)
95		initialize_pools();
96
97	if ((listp = uu_list_create(unresolved_pool, NULL, 0)) == NULL)
98		die("out of memory");
99
100	return (listp);
101}
102
103/*
104 * Adds the given name to the unresolved list.  'value' is an arbitrary value
105 * which is preserved for this entry, even when resolved against a target.  This
106 * allows the caller to associate similar behavior (such as the difference
107 * between -d, -D, and -s) without having to create multiple lists.
108 */
109void
110dis_namelist_add(dis_namelist_t *list, const char *name, int value)
111{
112	unresolved_name_t *node;
113
114	node = safe_malloc(sizeof (unresolved_name_t));
115
116	node->un_name = name;
117	node->un_value = value;
118	node->un_mark = 0;
119
120	(void) uu_list_insert_before(list, NULL, node);
121}
122
123/*
124 * Internal callback structure used
125 */
126typedef struct cb_data {
127	int		cb_mark;
128	uu_list_t	*cb_source;
129	uu_list_t	*cb_resolved;
130} cb_data_t;
131
132/*
133 * For each section, walk the list of unresolved names and resolve those that
134 * correspond to real functions.  We mark functions as we see them, and re-walk
135 * the list a second time to warn about functions we didn't find.
136 *
137 * This is an O(n * m) algorithm, but we typically search for only a single
138 * function.
139 */
140/* ARGSUSED */
141static void
142walk_sections(dis_tgt_t *tgt, dis_scn_t *scn, void *data)
143{
144	cb_data_t *cb = data;
145	unresolved_name_t *unp;
146	uu_list_walk_t *walk;
147
148	if ((walk = uu_list_walk_start(cb->cb_source, UU_DEFAULT)) == NULL)
149		die("out of memory");
150
151	while ((unp = uu_list_walk_next(walk)) != NULL) {
152		if (strcmp(unp->un_name, dis_section_name(scn)) == 0) {
153			resolved_name_t *resolved;
154
155			/*
156			 * Mark the current node as seen
157			 */
158			unp->un_mark = cb->cb_mark;
159
160			/*
161			 * Add the data to the resolved list
162			 */
163			resolved = safe_malloc(sizeof (resolved_name_t));
164
165			resolved->rn_data = dis_section_copy(scn);
166			resolved->rn_value = unp->un_value;
167
168			(void) uu_list_insert_before(cb->cb_resolved, NULL,
169			    resolved);
170		}
171	}
172
173	uu_list_walk_end(walk);
174}
175
176/*
177 * Take a list of unresolved names and create a resolved list of sections.  We
178 * rely on walk_sections() to do the dirty work.  After resolving the sections,
179 * we check for any unmarked names and warn the user about missing sections.
180 */
181dis_scnlist_t *
182dis_namelist_resolve_sections(dis_namelist_t *namelist, dis_tgt_t *tgt)
183{
184	uu_list_t *listp;
185	cb_data_t cb;
186	unresolved_name_t *unp;
187	uu_list_walk_t *walk;
188
189	/*
190	 * Walk all sections in the target, calling walk_sections() for each
191	 * one.
192	 */
193	if ((listp = uu_list_create(resolved_pool, NULL, UU_DEFAULT)) == NULL)
194		die("out of memory");
195
196	cb.cb_mark = ++current_mark;
197	cb.cb_source = namelist;
198	cb.cb_resolved = listp;
199
200	dis_tgt_section_iter(tgt, walk_sections, &cb);
201
202	/*
203	 * Walk all elements of the unresolved list, and report any that we
204	 * didn't mark in the process.
205	 */
206	if ((walk = uu_list_walk_start(namelist, UU_DEFAULT)) == NULL)
207		die("out of memory");
208
209	while ((unp = uu_list_walk_next(walk)) != NULL) {
210		if (unp->un_mark != current_mark)
211			warn("failed to find section '%s' in '%s'",
212			    unp->un_name, dis_tgt_name(tgt));
213	}
214
215	uu_list_walk_end(walk);
216
217	return (listp);
218}
219
220/*
221 * Similar to walk_sections(), but for functions.
222 */
223/* ARGSUSED */
224static void
225walk_functions(dis_tgt_t *tgt, dis_func_t *func, void *data)
226{
227	cb_data_t *cb = data;
228	unresolved_name_t *unp;
229	uu_list_walk_t *walk;
230
231	if ((walk = uu_list_walk_start(cb->cb_source, UU_DEFAULT)) == NULL)
232		die("out of memory");
233
234	while ((unp = uu_list_walk_next(walk)) != NULL) {
235		if (strcmp(unp->un_name, dis_function_name(func)) == 0) {
236			resolved_name_t *resolved;
237
238			unp->un_mark = cb->cb_mark;
239
240			resolved = safe_malloc(sizeof (resolved_name_t));
241
242			resolved->rn_data = dis_function_copy(func);
243			resolved->rn_value = unp->un_value;
244
245			(void) uu_list_insert_before(cb->cb_resolved, NULL,
246			    resolved);
247		}
248	}
249
250	uu_list_walk_end(walk);
251}
252
253/*
254 * Take a list of unresolved names and create a resolved list of functions.  We
255 * rely on walk_functions() to do the dirty work.  After resolving the
256 * functions, * we check for any unmarked names and warn the user about missing
257 * functions.
258 */
259dis_funclist_t *
260dis_namelist_resolve_functions(dis_namelist_t *namelist, dis_tgt_t *tgt)
261{
262	uu_list_t *listp;
263	uu_list_walk_t *walk;
264	unresolved_name_t *unp;
265	cb_data_t cb;
266
267	if ((listp = uu_list_create(resolved_pool, NULL, UU_DEFAULT)) == NULL)
268		die("out of memory");
269
270	cb.cb_mark = ++current_mark;
271	cb.cb_source = namelist;
272	cb.cb_resolved = listp;
273
274	dis_tgt_function_iter(tgt, walk_functions, &cb);
275
276	/*
277	 * Walk unresolved list and report any missing functions.
278	 */
279	if ((walk = uu_list_walk_start(namelist, UU_DEFAULT)) == NULL)
280		die("out of memory");
281
282	while ((unp = uu_list_walk_next(walk)) != NULL) {
283		if (unp->un_mark != current_mark)
284			warn("failed to find function '%s' in '%s'",
285			    unp->un_name, dis_tgt_name(tgt));
286	}
287
288	uu_list_walk_end(walk);
289
290	return (listp);
291}
292
293/*
294 * Returns true if the given list is empty.
295 */
296int
297dis_namelist_empty(dis_namelist_t *list)
298{
299	return (uu_list_numnodes(list) == 0);
300}
301
302static void
303free_list(uu_list_t *list)
304{
305	uu_list_walk_t *walk;
306	void *data;
307
308	if ((walk = uu_list_walk_start(list, UU_WALK_ROBUST)) == NULL)
309		die("out of memory");
310
311	while ((data = uu_list_walk_next(walk)) != NULL) {
312		uu_list_remove(list, data);
313		free(data);
314	}
315
316	uu_list_walk_end(walk);
317
318	uu_list_destroy(list);
319}
320
321/*
322 * Destroy a list of sections.  First, walk the list and free the associated
323 * section data.  Pass the list onto to free_list() to clean up the rest of the
324 * list.
325 */
326void
327dis_scnlist_destroy(dis_scnlist_t *list)
328{
329	uu_list_walk_t *walk;
330	resolved_name_t *data;
331
332	if ((walk = uu_list_walk_start(list, UU_DEFAULT)) == NULL)
333		die("out of memory");
334
335	while ((data = uu_list_walk_next(walk)) != NULL)
336		dis_section_free(data->rn_data);
337
338	uu_list_walk_end(walk);
339
340	free_list(list);
341}
342
343/*
344 * Destroy a list of functions.  First, walk the list and free the associated
345 * function data.  Pass the list onto to free_list() to clean up the rest of the
346 * list.
347 */
348void
349dis_funclist_destroy(dis_funclist_t *list)
350{
351	uu_list_walk_t *walk;
352	resolved_name_t *data;
353
354	if ((walk = uu_list_walk_start(list, UU_DEFAULT)) == NULL)
355		die("out of memory");
356
357	while ((data = uu_list_walk_next(walk)) != NULL)
358		dis_function_free(data->rn_data);
359
360	uu_list_walk_end(walk);
361
362	free_list(list);
363}
364
365/*
366 * Destroy a lis tof unresolved names.
367 */
368void
369dis_namelist_destroy(dis_namelist_t *list)
370{
371	free_list(list);
372}
373
374/*
375 * Iterate over a resolved list of sections.
376 */
377void
378dis_scnlist_iter(uu_list_t *list, void (*func)(dis_scn_t *, int, void *),
379    void *arg)
380{
381	uu_list_walk_t *walk;
382	resolved_name_t *data;
383
384	if ((walk = uu_list_walk_start(list, UU_DEFAULT)) == NULL)
385		die("out of memory");
386
387	while ((data = uu_list_walk_next(walk)) != NULL)
388		func(data->rn_data, data->rn_value, arg);
389
390	uu_list_walk_end(walk);
391}
392
393/*
394 * Iterate over a resolved list of functions.
395 */
396void
397dis_funclist_iter(uu_list_t *list, void (*func)(dis_func_t *, int, void *),
398    void *arg)
399{
400	uu_list_walk_t *walk;
401	resolved_name_t *data;
402
403	if ((walk = uu_list_walk_start(list, UU_DEFAULT)) == NULL)
404		die("out of memory");
405
406	while ((data = uu_list_walk_next(walk)) != NULL)
407		func(data->rn_data, data->rn_value, arg);
408
409	uu_list_walk_end(walk);
410}
411