/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include "dis_target.h" #include "dis_list.h" #include "dis_util.h" /* * List support functions. * * Support routines for managing lists of sections and functions. We first * process the command line arguments into lists of strings. For each target, * we resolve these strings against the set of available sections and/or * functions to arrive at the set of objects to disassemble. * * We export two types of lists, namelists and resolvelists. The first is used * to record names given as command line options. The latter is used to * maintain the data objects specific to a given target. */ typedef struct unresolved_name { const char *un_name; /* name of function or object */ int un_value; /* user-supplied data */ int un_mark; /* internal counter */ uu_list_node_t un_node; /* uulist node */ } unresolved_name_t; typedef struct resolved_name { void *rn_data; /* section or function data */ int rn_value; /* user-supplied data */ uu_list_node_t rn_node; /* uulist node */ } resolved_name_t; static uu_list_pool_t *unresolved_pool; static uu_list_pool_t *resolved_pool; static int current_mark = 0; static void initialize_pools(void) { unresolved_pool = uu_list_pool_create( "unresolved_pool", sizeof (unresolved_name_t), offsetof(unresolved_name_t, un_node), NULL, 0); resolved_pool = uu_list_pool_create( "resolved_pool", sizeof (resolved_name_t), offsetof(resolved_name_t, rn_node), NULL, 0); if (unresolved_pool == NULL || resolved_pool == NULL) die("out of memory"); } /* * Returns an empty list of unresolved names. */ dis_namelist_t * dis_namelist_create(void) { uu_list_t *listp; /* * If this is the first request to create a list, initialize the list * pools. */ if (unresolved_pool == NULL) initialize_pools(); if ((listp = uu_list_create(unresolved_pool, NULL, 0)) == NULL) die("out of memory"); return (listp); } /* * Adds the given name to the unresolved list. 'value' is an arbitrary value * which is preserved for this entry, even when resolved against a target. This * allows the caller to associate similar behavior (such as the difference * between -d, -D, and -s) without having to create multiple lists. */ void dis_namelist_add(dis_namelist_t *list, const char *name, int value) { unresolved_name_t *node; node = safe_malloc(sizeof (unresolved_name_t)); node->un_name = name; node->un_value = value; node->un_mark = 0; (void) uu_list_insert_before(list, NULL, node); } /* * Internal callback structure used */ typedef struct cb_data { int cb_mark; uu_list_t *cb_source; uu_list_t *cb_resolved; } cb_data_t; /* * For each section, walk the list of unresolved names and resolve those that * correspond to real functions. We mark functions as we see them, and re-walk * the list a second time to warn about functions we didn't find. * * This is an O(n * m) algorithm, but we typically search for only a single * function. */ /* ARGSUSED */ static void walk_sections(dis_tgt_t *tgt, dis_scn_t *scn, void *data) { cb_data_t *cb = data; unresolved_name_t *unp; uu_list_walk_t *walk; if ((walk = uu_list_walk_start(cb->cb_source, UU_DEFAULT)) == NULL) die("out of memory"); while ((unp = uu_list_walk_next(walk)) != NULL) { if (strcmp(unp->un_name, dis_section_name(scn)) == 0) { resolved_name_t *resolved; /* * Mark the current node as seen */ unp->un_mark = cb->cb_mark; /* * Add the data to the resolved list */ resolved = safe_malloc(sizeof (resolved_name_t)); resolved->rn_data = dis_section_copy(scn); resolved->rn_value = unp->un_value; (void) uu_list_insert_before(cb->cb_resolved, NULL, resolved); } } uu_list_walk_end(walk); } /* * Take a list of unresolved names and create a resolved list of sections. We * rely on walk_sections() to do the dirty work. After resolving the sections, * we check for any unmarked names and warn the user about missing sections. */ dis_scnlist_t * dis_namelist_resolve_sections(dis_namelist_t *namelist, dis_tgt_t *tgt) { uu_list_t *listp; cb_data_t cb; unresolved_name_t *unp; uu_list_walk_t *walk; /* * Walk all sections in the target, calling walk_sections() for each * one. */ if ((listp = uu_list_create(resolved_pool, NULL, UU_DEFAULT)) == NULL) die("out of memory"); cb.cb_mark = ++current_mark; cb.cb_source = namelist; cb.cb_resolved = listp; dis_tgt_section_iter(tgt, walk_sections, &cb); /* * Walk all elements of the unresolved list, and report any that we * didn't mark in the process. */ if ((walk = uu_list_walk_start(namelist, UU_DEFAULT)) == NULL) die("out of memory"); while ((unp = uu_list_walk_next(walk)) != NULL) { if (unp->un_mark != current_mark) warn("failed to find section '%s' in '%s'", unp->un_name, dis_tgt_name(tgt)); } uu_list_walk_end(walk); return (listp); } /* * Similar to walk_sections(), but for functions. */ /* ARGSUSED */ static void walk_functions(dis_tgt_t *tgt, dis_func_t *func, void *data) { cb_data_t *cb = data; unresolved_name_t *unp; uu_list_walk_t *walk; if ((walk = uu_list_walk_start(cb->cb_source, UU_DEFAULT)) == NULL) die("out of memory"); while ((unp = uu_list_walk_next(walk)) != NULL) { if (strcmp(unp->un_name, dis_function_name(func)) == 0) { resolved_name_t *resolved; unp->un_mark = cb->cb_mark; resolved = safe_malloc(sizeof (resolved_name_t)); resolved->rn_data = dis_function_copy(func); resolved->rn_value = unp->un_value; (void) uu_list_insert_before(cb->cb_resolved, NULL, resolved); } } uu_list_walk_end(walk); } /* * Take a list of unresolved names and create a resolved list of functions. We * rely on walk_functions() to do the dirty work. After resolving the * functions, * we check for any unmarked names and warn the user about missing * functions. */ dis_funclist_t * dis_namelist_resolve_functions(dis_namelist_t *namelist, dis_tgt_t *tgt) { uu_list_t *listp; uu_list_walk_t *walk; unresolved_name_t *unp; cb_data_t cb; if ((listp = uu_list_create(resolved_pool, NULL, UU_DEFAULT)) == NULL) die("out of memory"); cb.cb_mark = ++current_mark; cb.cb_source = namelist; cb.cb_resolved = listp; dis_tgt_function_iter(tgt, walk_functions, &cb); /* * Walk unresolved list and report any missing functions. */ if ((walk = uu_list_walk_start(namelist, UU_DEFAULT)) == NULL) die("out of memory"); while ((unp = uu_list_walk_next(walk)) != NULL) { if (unp->un_mark != current_mark) warn("failed to find function '%s' in '%s'", unp->un_name, dis_tgt_name(tgt)); } uu_list_walk_end(walk); return (listp); } /* * Returns true if the given list is empty. */ int dis_namelist_empty(dis_namelist_t *list) { return (uu_list_numnodes(list) == 0); } static void free_list(uu_list_t *list) { uu_list_walk_t *walk; void *data; if ((walk = uu_list_walk_start(list, UU_WALK_ROBUST)) == NULL) die("out of memory"); while ((data = uu_list_walk_next(walk)) != NULL) { uu_list_remove(list, data); free(data); } uu_list_walk_end(walk); uu_list_destroy(list); } /* * Destroy a list of sections. First, walk the list and free the associated * section data. Pass the list onto to free_list() to clean up the rest of the * list. */ void dis_scnlist_destroy(dis_scnlist_t *list) { uu_list_walk_t *walk; resolved_name_t *data; if ((walk = uu_list_walk_start(list, UU_DEFAULT)) == NULL) die("out of memory"); while ((data = uu_list_walk_next(walk)) != NULL) dis_section_free(data->rn_data); uu_list_walk_end(walk); free_list(list); } /* * Destroy a list of functions. First, walk the list and free the associated * function data. Pass the list onto to free_list() to clean up the rest of the * list. */ void dis_funclist_destroy(dis_funclist_t *list) { uu_list_walk_t *walk; resolved_name_t *data; if ((walk = uu_list_walk_start(list, UU_DEFAULT)) == NULL) die("out of memory"); while ((data = uu_list_walk_next(walk)) != NULL) dis_function_free(data->rn_data); uu_list_walk_end(walk); free_list(list); } /* * Destroy a lis tof unresolved names. */ void dis_namelist_destroy(dis_namelist_t *list) { free_list(list); } /* * Iterate over a resolved list of sections. */ void dis_scnlist_iter(uu_list_t *list, void (*func)(dis_scn_t *, int, void *), void *arg) { uu_list_walk_t *walk; resolved_name_t *data; if ((walk = uu_list_walk_start(list, UU_DEFAULT)) == NULL) die("out of memory"); while ((data = uu_list_walk_next(walk)) != NULL) func(data->rn_data, data->rn_value, arg); uu_list_walk_end(walk); } /* * Iterate over a resolved list of functions. */ void dis_funclist_iter(uu_list_t *list, void (*func)(dis_func_t *, int, void *), void *arg) { uu_list_walk_t *walk; resolved_name_t *data; if ((walk = uu_list_walk_start(list, UU_DEFAULT)) == NULL) die("out of memory"); while ((data = uu_list_walk_next(walk)) != NULL) func(data->rn_data, data->rn_value, arg); uu_list_walk_end(walk); }