/*
 * 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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
#pragma ident	"%Z%%M%	%I%	%E% SMI"

#define	ELF_TARGET_AMD64	/* SHN_AMD64_LCOMMON */

#include	<stdio.h>
#include	<unistd.h>
#include	<elfedit.h>
#include	<strings.h>
#include	<debug.h>
#include	<conv.h>
#include	<sym_msg.h>




#define	MAXNDXSIZE	10



/*
 * This module uses shared code for several of the commands.
 * It is sometimes necessary to know which specific command
 * is active.
 */
typedef enum {
	SYM_CMD_T_DUMP =		0,	/* sym:dump */

	SYM_CMD_T_ST_BIND =		1,	/* sym:st_bind */
	SYM_CMD_T_ST_INFO =		2,	/* sym:st_info */
	SYM_CMD_T_ST_NAME =		3,	/* sym:st_name */
	SYM_CMD_T_ST_OTHER =		4,	/* sym:st_other */
	SYM_CMD_T_ST_SHNDX =		5,	/* sym:st_shndx */
	SYM_CMD_T_ST_SIZE =		6,	/* sym:st_size */
	SYM_CMD_T_ST_TYPE =		7,	/* sym:st_type */
	SYM_CMD_T_ST_VALUE =		8,	/* sym:st_value */
	SYM_CMD_T_ST_VISIBILITY =	9	/* sym:st_visibility */
} SYM_CMD_T;



/*
 * ELFCLASS-specific definitions
 */
#ifdef _ELF64

#define	MSG_FMT_XWORDVALNL MSG_FMT_XWORDVALNL_64

#else

#define	MSG_FMT_XWORDVALNL MSG_FMT_XWORDVALNL_32

/*
 * We supply this function for the msg module. Only one copy is needed.
 */
const char *
_sym_msg(Msg mid)
{
	return (gettext(MSG_ORIG(mid)));
}

#endif



/*
 * This function is supplied to elfedit through our elfedit_module_t
 * definition. It translates the opaque elfedit_i18nhdl_t handles
 * in our module interface into the actual strings for elfedit to
 * use.
 *
 * note:
 *	This module uses Msg codes for its i18n handle type.
 *	So the translation is simply to use MSG_INTL() to turn
 *	it into a string and return it.
 */
static const char *
mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
{
	Msg msg = (Msg)hdl;

	return (MSG_INTL(msg));
}



/*
 * The sym_opt_t enum specifies a bit value for every optional
 * argument allowed by a command in this module.
 */
typedef enum {
	SYM_OPT_F_XSHINDEX =	1,	/* -e: Force shndx update to extended */
					/*	 index section */
	SYM_OPT_F_NAMOFFSET =	2,	/* -name_offset: sym:st_name name arg */
					/*	is numeric offset */
					/* 	rather than ASCII string */
	SYM_OPT_F_SECSHNDX =	4,	/* -secshndx: Section arg is */
					/*	section index, not name */
	SYM_OPT_F_SECSHTYP =	8,	/* -secshtyp: Section arg is */
					/*	section type, not name */
	SYM_OPT_F_SHNAME =	16,	/* -shnam name: section spec. by name */
	SYM_OPT_F_SHNDX =	32,	/* -shndx ndx: section spec. by index */
	SYM_OPT_F_SHTYP =	64,	/* -shtyp type: section spec. by type */
	SYM_OPT_F_SYMNDX =	128	/* -symndx: Sym specified by index */
} sym_opt_t;


/*
 * A variable of type ARGSTATE is used by each command to maintain
 * the overall state for a given set of arguments and the symbol tables
 * being managed.
 *
 * The state for each symbol table and the auxiliary sections that are
 * related to it are kept in a SYMSTATE sub-struct.
 *
 * One benefit of ARGSTATE is that it helps us to ensure that we only
 * fetch each section a single time:
 *	- More efficient
 *	- Prevents multiple ELFEDIT_MSG_DEBUG messages from
 *	  being produced for a given section.
 *
 * note: The symstate array in ARGSTATE is defined as having one
 *	element, but in reality, we allocate enough room for
 *	the number of elements defined in the numsymstate field.
 */
typedef struct {
	Word ndx;	/* If argstate.argc > 0, this is the table index */
	struct {				/* Symbol table */
		elfedit_section_t	*sec;
		Sym			*data;
		Word			n;
	} sym;
	struct {				/* String table */
		elfedit_section_t	*sec;
	} str;
	struct {				/* Versym */
		Word			shndx;
		elfedit_section_t	*sec;
		Versym			*data;
		Word			n;
	} versym;
	struct {				/* Extended section indices */
		Word			shndx;
		elfedit_section_t	*sec;
		Word			*data;
		Word			n;
	} xshndx;
} SYMSTATE;
typedef struct {
	elfedit_obj_state_t	*obj_state;
	sym_opt_t		optmask;   	/* Mask of options used */
	int			argc;		/* # of plain arguments */
	const char		**argv;		/* Plain arguments */
	int			numsymstate;	/* # of items in symstate[] */
	SYMSTATE		symstate[1];	/* Symbol tables to process */
} ARGSTATE;


/*
 * We maintain the state of each symbol table and related associated
 * sections in a SYMSTATE structure . We don't look those auxiliary
 * things up unless we actually need them, both to be efficient,
 * and to prevent duplicate ELFEDIT_MSG_DEBUG messages from being
 * issued as they are located. Hence, process_args() is used to
 * initialize the state block with just the symbol table, and then one
 * of the argstate_add_XXX() functions is used as needed
 * to fetch the additional sections.
 *
 * entry:
 *	argstate - Overall state block
 *	symstate - State block for current symbol table.
 *
 * exit:
 *	If the needed auxiliary section is not found, an error is
 *	issued and the argstate_add_XXX() routine does not return.
 *	Otherwise, the fields in argstate have been filled in, ready
 *	for use.
 *
 */
static void
symstate_add_str(ARGSTATE *argstate, SYMSTATE *symstate)
{
	if (symstate->str.sec != NULL)
		return;

	symstate->str.sec = elfedit_sec_getstr(argstate->obj_state,
	    symstate->sym.sec->sec_shdr->sh_link, 0);
}
static void
symstate_add_versym(ARGSTATE *argstate, SYMSTATE *symstate)
{
	if (symstate->versym.sec != NULL)
		return;

	symstate->versym.sec = elfedit_sec_getversym(argstate->obj_state,
	    symstate->sym.sec, &symstate->versym.data, &symstate->versym.n);
}
static void
symstate_add_xshndx(ARGSTATE *argstate, SYMSTATE *symstate)
{
	if (symstate->xshndx.sec != NULL)
		return;

	symstate->xshndx.sec = elfedit_sec_getxshndx(argstate->obj_state,
	    symstate->sym.sec, &symstate->xshndx.data, &symstate->xshndx.n);
}



/*
 * Display symbol table entries in the style used by elfdump.
 *
 * entry:
 *	argstate - Overall state block
 *	symstate - State block for current symbol table.
 *	ndx - Index of first symbol to display
 *	cnt - Number of symbols to display
 */
static void
dump_symtab(ARGSTATE *argstate, SYMSTATE *symstate, Word ndx, Word cnt)
{
	char			index[MAXNDXSIZE];
	Word			shndx;
	const char		*shndx_name;
	elfedit_section_t	*symsec;
	elfedit_section_t	*strsec;
	Sym			*sym;
	elfedit_obj_state_t	*obj_state = argstate->obj_state;
	Half			mach = obj_state->os_ehdr->e_machine;
	const char		*symname;
	Versym			versym;

	symsec = symstate->sym.sec;
	sym = symstate->sym.data + ndx;

	symstate_add_str(argstate, symstate);
	strsec = symstate->str.sec;

	/* If there is a versym index section, fetch it */
	if (symstate->versym.shndx != SHN_UNDEF)
		symstate_add_versym(argstate, symstate);

	/* If there is an extended index section, fetch it */
	if (symstate->xshndx.shndx != SHN_UNDEF)
		symstate_add_xshndx(argstate, symstate);

	elfedit_printf(MSG_INTL(MSG_FMT_SYMTAB), symsec->sec_name);
	Elf_syms_table_title(0, ELF_DBG_ELFDUMP);
	for (; cnt-- > 0; ndx++, sym++) {
		(void) snprintf(index, MAXNDXSIZE,
		    MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(ndx));
		versym = (symstate->versym.sec == NULL) ? 0 :
		    symstate->versym.data[ndx];
		symname = elfedit_offset_to_str(strsec, sym->st_name,
		    ELFEDIT_MSG_DEBUG, 0);
		shndx = sym->st_shndx;
		if ((shndx == SHN_XINDEX) && (symstate->xshndx.sec != NULL))
			shndx = symstate->xshndx.data[ndx];
		shndx_name = elfedit_shndx_to_name(obj_state, shndx);
		Elf_syms_table_entry(NULL, ELF_DBG_ELFDUMP, index, mach,
		    sym, versym, 0, shndx_name, symname);
	}
}



/*
 * Called by print_sym() to determine if a given symbol has the same
 * display value for the current command in every symbol table.
 *
 * entry:
 *	cmd - SYM_CMD_T_* value giving identify of caller
 *	argstate - Overall state block
 *	outstyle - Output style to use
 */
static int
all_same(SYM_CMD_T cmd, ARGSTATE *argstate, elfedit_outstyle_t outstyle)
{
	Word			tblndx;
	SYMSTATE		*symstate1, *symstate2;
	Sym			*sym1, *sym2;

	symstate1 = argstate->symstate;
	for (tblndx = 0; tblndx < (argstate->numsymstate - 1);
	    tblndx++, symstate1++) {
		symstate2 = symstate1 + 1;
		sym1 = &symstate1->sym.data[symstate1->ndx];
		sym2 = &symstate2->sym.data[symstate2->ndx];

		switch (cmd) {
		case SYM_CMD_T_DUMP:
			/* sym:dump should always show everything */
			return (0);

		case SYM_CMD_T_ST_BIND:
			if (ELF_ST_BIND(sym1->st_info) !=
			    ELF_ST_BIND(sym2->st_info))
				return (0);
			break;

		case SYM_CMD_T_ST_INFO:
			if (sym1->st_info !=  sym2->st_info)
				return (0);
			break;

		case SYM_CMD_T_ST_NAME:
			/*
			 * In simple output mode, we show the string. In
			 * numeric mode, we show the string table offset.
			 */
			if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
				const char *n1, *n2;

				symstate_add_str(argstate, symstate1);
				symstate_add_str(argstate, symstate2);
				n1 = elfedit_offset_to_str(symstate1->str.sec,
				    sym1->st_name, ELFEDIT_MSG_DEBUG, 0);
				n2 = elfedit_offset_to_str(symstate2->str.sec,
				    sym2->st_name, ELFEDIT_MSG_DEBUG, 0);
				if (strcmp(n1, n2) != 0)
					return (0);
			} else {
				if (sym1->st_name !=  sym2->st_name)
					return (0);
			}
			break;

		case SYM_CMD_T_ST_OTHER:
			if (sym1->st_other !=  sym2->st_other)
				return (0);
			break;

		case SYM_CMD_T_ST_SHNDX:
			{
				Word	ndx1, ndx2;

				ndx1 = sym1->st_shndx;
				if ((ndx1 == SHN_XINDEX) &&
				    (symstate1->xshndx.shndx != SHN_UNDEF)) {
					symstate_add_xshndx(argstate,
					    symstate1);
					ndx1 = symstate1->xshndx.
					    data[symstate1->ndx];
				}
				ndx2 = sym2->st_shndx;
				if ((ndx2 == SHN_XINDEX) &&
				    (symstate2->xshndx.shndx != SHN_UNDEF)) {
					symstate_add_xshndx(argstate,
					    symstate2);
					ndx2 = symstate2->xshndx.
					    data[symstate2->ndx];
				}
				if (ndx1 !=  ndx2)
					return (0);
			}
			break;

		case SYM_CMD_T_ST_SIZE:
			if (sym1->st_size !=  sym2->st_size)
				return (0);
			break;

		case SYM_CMD_T_ST_TYPE:
			if (ELF_ST_TYPE(sym1->st_info) !=
			    ELF_ST_TYPE(sym2->st_info))
				return (0);
			break;

		case SYM_CMD_T_ST_VALUE:
			if (sym1->st_value !=  sym2->st_value)
				return (0);
			break;

		case SYM_CMD_T_ST_VISIBILITY:
			if (ELF_ST_VISIBILITY(sym1->st_info) !=
			    ELF_ST_VISIBILITY(sym2->st_info))
				return (0);
			break;
		}
	}

	/* If we got here, there are no differences (or maybe only 1 table */
	return (1);
}


/*
 * Called by print_sym() to display values for a single symbol table.
 *
 * entry:
 *	autoprint - If True, output is only produced if the elfedit
 *		autoprint flag is set. If False, output is always produced.
 *	cmd - SYM_CMD_T_* value giving identify of caller
 *	argstate - Overall state block
 *	symstate - State block for current symbol table.
 *	ndx - Index of first symbol to display
 *	cnt - Number of symbols to display
 */
static void
print_symstate(SYM_CMD_T cmd, ARGSTATE *argstate, SYMSTATE *symstate,
    elfedit_outstyle_t outstyle, Word ndx, Word cnt)
{
	Word	value;
	Sym	*sym;

	/*
	 * If doing default output, use elfdump style where we
	 * show all symbol attributes. In this case, the command
	 * that called us doesn't matter
	 */
	if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
		dump_symtab(argstate, symstate, ndx, cnt);
		return;
	}

	sym = symstate->sym.data;

	switch (cmd) {
	case SYM_CMD_T_ST_BIND:
		{
			Conv_inv_buf_t inv_buf;

			for (sym += ndx; cnt--; sym++) {
				value = ELF_ST_BIND(sym->st_info);
				if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
					    conv_sym_info_bind(value,
					    CONV_FMT_ALT_FULLNAME, &inv_buf));
				} else {
					elfedit_printf(
					    MSG_ORIG(MSG_FMT_WORDVALNL),
					    EC_WORD(value));
				}
			}
		}
		return;

	case SYM_CMD_T_ST_INFO:
		for (sym += ndx; cnt-- > 0; sym++)
			elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL),
			    EC_WORD(sym->st_info));
		return;

	case SYM_CMD_T_ST_NAME:
		/*
		 * In simple output mode, we show the string. In numeric
		 * mode, we show the string table offset.
		 */
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
			symstate_add_str(argstate, symstate);
			for (sym += ndx; cnt--; sym++) {
				elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
				    elfedit_offset_to_str(symstate->str.sec,
				    sym->st_name, ELFEDIT_MSG_ERR, 0));
			}
		} else {
			for (; cnt--; sym++)
				elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL),
				    EC_WORD(sym->st_name));
		}
		return;

	case SYM_CMD_T_ST_OTHER:
		for (sym += ndx; cnt-- > 0; sym++)
			elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL),
			    EC_WORD(sym->st_other));
		return;

	case SYM_CMD_T_ST_SHNDX:
		/* If there is an extended index section, fetch it */
		if (symstate->xshndx.shndx != SHN_UNDEF)
			symstate_add_xshndx(argstate, symstate);

		for (; cnt--; ndx++) {
			value = sym[ndx].st_shndx;
			if ((value == SHN_XINDEX) &&
			    (symstate->xshndx.sec != NULL))
				value = symstate->xshndx.data[ndx];

			if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
				elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
				    elfedit_shndx_to_name(argstate->obj_state,
				    value));
			} else {
				elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL),
				    EC_WORD(value));
			}
		}
		return;

	case SYM_CMD_T_ST_SIZE:
		/*
		 * machine word width integers displayed in fixed width
		 * 0-filled hex format.
		 */
		for (sym += ndx; cnt--; sym++)
			elfedit_printf(MSG_ORIG(MSG_FMT_XWORDVALNL),
			    sym->st_size);
		return;

	case SYM_CMD_T_ST_TYPE:
		{
			Half mach = argstate->obj_state->os_ehdr->e_machine;
			Conv_inv_buf_t inv_buf;

			for (sym += ndx; cnt--; sym++) {
				value = ELF_ST_TYPE(sym->st_info);
				if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
					    conv_sym_info_type(mach, value,
					    CONV_FMT_ALT_FULLNAME, &inv_buf));
				} else {
					elfedit_printf(
					    MSG_ORIG(MSG_FMT_WORDVALNL),
					    EC_WORD(value));
				}
			}
		}
		return;

	case SYM_CMD_T_ST_VALUE:
		/*
		 * machine word width integers displayed in fixed width
		 * 0-filled hex format.
		 */
		for (sym += ndx; cnt--; sym++)
			elfedit_printf(MSG_ORIG(MSG_FMT_XWORDVALNL),
			    sym->st_value);
		return;

	case SYM_CMD_T_ST_VISIBILITY:
		{
			Conv_inv_buf_t inv_buf;

			for (sym += ndx; cnt--; sym++) {
				value = ELF_ST_VISIBILITY(sym->st_other);
				if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
					    conv_sym_other_vis(value,
					    CONV_FMT_ALT_FULLNAME, &inv_buf));
				} else {
					elfedit_printf(
					    MSG_ORIG(MSG_FMT_WORDVALNL),
					    EC_WORD(value));
				}
			}
		}
		return;

	}
}


/*
 * Print symbol values, taking the calling command, and output style
 * into account.
 *
 * entry:
 *	autoprint - If True, output is only produced if the elfedit
 *		autoprint flag is set. If False, output is always produced.
 *	cmd - SYM_CMD_T_* value giving identify of caller
 *	argstate - Overall state block
 *	symstate - State block for current symbol table.
 *	ndx - Index of first symbol to display
 *	cnt - Number of symbols to display
 */
static void
print_sym(SYM_CMD_T cmd, int autoprint, ARGSTATE *argstate)
{
	Word			ndx, tblndx;
	Word			cnt;
	elfedit_outstyle_t	outstyle;
	SYMSTATE		*symstate;
	int			only_one;

	if ((autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0)))
		return;

	/*
	 * Pick an output style. sym:dump is required to use the default
	 * style. The other commands use the current output style.
	 */
	outstyle = (cmd == SYM_CMD_T_DUMP) ?
	    ELFEDIT_OUTSTYLE_DEFAULT : elfedit_outstyle();

	/*
	 * This is a nicity: Force any needed auxiliary sections to be
	 * fetched here before any output is produced. This will put all
	 * of the debug messages right at the top in a single cluster.
	 */
	symstate = argstate->symstate;
	for (tblndx = 0; tblndx < argstate->numsymstate; tblndx++, symstate++) {
		if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
			symstate_add_str(argstate, symstate);
			if (symstate->versym.shndx != SHN_UNDEF)
				symstate_add_versym(argstate, symstate);
			if (symstate->xshndx.shndx != SHN_UNDEF)
				symstate_add_xshndx(argstate, symstate);
			continue;
		}

		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
			switch (cmd) {
			case SYM_CMD_T_ST_NAME:
				symstate_add_str(argstate, symstate);
				break;

			case SYM_CMD_T_ST_SHNDX:
				if (symstate->xshndx.shndx != SHN_UNDEF)
					symstate_add_xshndx(argstate, symstate);
				break;
			}
		}
	}

	/*
	 * If there is more than one table, we are displaying a single
	 * item, we are not using the default "elfdump" style, and all
	 * the symbols have the same value for the thing we intend to
	 * display, then we only want to display it once.
	 */
	only_one = (argstate->numsymstate > 1) && (argstate->argc > 0) &&
	    (outstyle != ELFEDIT_OUTSTYLE_DEFAULT) &&
	    all_same(cmd, argstate, outstyle);

	/* Run through the tables and display from each one */
	symstate = argstate->symstate;
	for (tblndx = 0; tblndx < argstate->numsymstate; tblndx++, symstate++) {
		if (argstate->argc == 0) {
			ndx = 0;
			cnt = symstate->sym.n;
		} else {
			ndx = symstate->ndx;
			cnt = 1;
		}

		if ((tblndx > 0) && ((argstate->argc == 0) ||
		    (outstyle == ELFEDIT_OUTSTYLE_DEFAULT)))
			elfedit_printf(MSG_ORIG(MSG_STR_NL));

		print_symstate(cmd, argstate, symstate, outstyle, ndx, cnt);
		if (only_one)
			break;
	}
}


/*
 * The cmd_body_set_st_XXX() functions are for use by cmd_body().
 * They handle the case where the second plain argument is
 * a value to be stored in the symbol.
 *
 * entry:
 *	argstate - Overall state block
 *	symstate - State block for current symbol table.
 */
static elfedit_cmdret_t
cmd_body_set_st_bind(ARGSTATE *argstate, SYMSTATE *symstate)
{
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	Sym			*sym = &symstate->sym.data[symstate->ndx];
	Word			gbl_ndx;
	uchar_t			bind, type, old_bind;
	Word			symndx;
	Conv_inv_buf_t		inv_buf1, inv_buf2;

	/*
	 * Use the ELF_ST_BIND() macro to access the defined bits
	 * of the st_info field related to symbol binding.
	 * Accepts STB_ symbolic names as well as integers.
	 */
	bind = elfedit_atoconst_range(argstate->argv[1],
	    MSG_INTL(MSG_ARG_SYMBIND), 0, 15, ELFEDIT_CONST_STB);
	old_bind = ELF_ST_BIND(sym->st_info);
	type = ELF_ST_TYPE(sym->st_info);

	if (old_bind == bind) {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_BIND),
		    conv_sym_info_bind(bind, CONV_FMT_ALT_FULLNAME, &inv_buf1));
	} else {
		/*
		 * The sh_info field of the symbol table section header
		 * gives the index of the first non-local symbol in
		 * the table. Issue warnings if the binding we set
		 * contradicts this.
		 */
		gbl_ndx = symstate->sym.sec->sec_shdr->sh_info;
		symndx = symstate->sym.sec->sec_shndx;
		if ((bind == STB_LOCAL) && (symstate->ndx >= gbl_ndx))
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_LBINDGSYM),
			    EC_WORD(symndx), symstate->sym.sec->sec_name,
			    symstate->ndx, EC_WORD(symndx), gbl_ndx);
		if ((bind != STB_LOCAL) && (symstate->ndx < gbl_ndx))
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_GBINDLSYM),
			    EC_WORD(symndx), symstate->sym.sec->sec_name,
			    symstate->ndx, EC_WORD(symndx), gbl_ndx);

		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_BIND),
		    conv_sym_info_bind(old_bind, CONV_FMT_ALT_FULLNAME,
		    &inv_buf1),
		    conv_sym_info_bind(bind, CONV_FMT_ALT_FULLNAME, &inv_buf2));
		ret = ELFEDIT_CMDRET_MOD;
		sym->st_info = ELF_ST_INFO(bind, type);
	}

	return (ret);
}

static elfedit_cmdret_t
cmd_body_set_st_name(ARGSTATE *argstate, SYMSTATE *symstate)
{
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	Sym			*sym = &symstate->sym.data[symstate->ndx];
	Word	str_offset;

	/*
	 * If -n was specified, this is an offset into the string
	 * table. Otherwise it is a string we need to turn into
	 * an offset
	 */
	symstate_add_str(argstate, symstate);
	if (argstate->optmask & SYM_OPT_F_NAMOFFSET) {
		str_offset = elfedit_atoui(argstate->argv[1], NULL);
		/* Warn if the offset is out of range */
		(void) elfedit_offset_to_str(symstate->str.sec,
		    str_offset, ELFEDIT_MSG_DEBUG, 1);
	} else {
		str_offset = elfedit_strtab_insert(argstate->obj_state,
		    symstate->str.sec, NULL, argstate->argv[1]);
	}

	if (sym->st_name == str_offset) {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_D_OK),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_NAME),
		    EC_WORD(sym->st_name));
	} else {
		/*
		 * Warn the user: Changing the name of a symbol in the dynsym
		 * will break the hash table in this object.
		 */
		if (symstate->sym.sec->sec_shdr->sh_type == SHT_DYNSYM)
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_DYNSYMNAMCHG),
			    EC_WORD(symstate->sym.sec->sec_shndx),
			    symstate->sym.sec->sec_name,
			    EC_WORD(symstate->ndx));

		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_D_CHG),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_NAME),
		    EC_WORD(sym->st_name),
		    EC_WORD(str_offset));
		ret = ELFEDIT_CMDRET_MOD;
		sym->st_name = str_offset;
	}

	return (ret);
}

static elfedit_cmdret_t
cmd_body_set_st_shndx(ARGSTATE *argstate, SYMSTATE *symstate)
{
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	Sym			*sym = &symstate->sym.data[symstate->ndx];
	Word	shndx, st_shndx, xshndx;
	int	use_xshndx;
	int	shndx_chg, xshndx_chg;


	/*
	 * By default, the sec argument is a section name. If -secshndx was
	 * specified, it is a section index, and if -secshtyp is specified,
	 * it is a section type.
	 */
	if (argstate->optmask & SYM_OPT_F_SECSHNDX)
		shndx = elfedit_atoshndx(argstate->argv[1],
		    argstate->obj_state->os_shnum);
	else if (argstate->optmask & SYM_OPT_F_SECSHTYP)
		shndx = elfedit_type_to_shndx(argstate->obj_state,
		    elfedit_atoconst(argstate->argv[1], ELFEDIT_CONST_SHT));
	else
		shndx = elfedit_name_to_shndx(argstate->obj_state,
		    argstate->argv[1]);

	/*
	 * We want to use an extended index section if the index is too
	 * large to be represented otherwise, or if the caller specified
	 * the -e option to make us do it anyway. However, we cannot
	 * do this if the index is in the special reserved range between
	 * SHN_LORESERVE and SHN_HIRESERVE.
	 */
	use_xshndx = (shndx > SHN_HIRESERVE) ||
	    ((shndx < SHN_LORESERVE) &&
	    (argstate->optmask & SYM_OPT_F_XSHINDEX));

	/*
	 * There are two cases where we have to touch the extended
	 * index section:
	 *
	 *	1) We have determined that we need to, as determined above.
	 *	2) We do not require it, but the file has an extended
	 *		index section, in which case we should set the slot
	 *		in that extended section to SHN_UNDEF (0).
	 *
	 * Fetch the extended section as required, and determine the values
	 * for st_shndx and the extended section slot.
	 */
	if (use_xshndx) {
		/* We must have an extended index section, or error out */
		symstate_add_xshndx(argstate, symstate);

		/* Set symbol to SHN_XINDEX, put index in the extended sec. */
		st_shndx = SHN_XINDEX;
		xshndx = shndx;
	} else {
		st_shndx = shndx;
		xshndx = SHN_UNDEF;
		if (symstate->xshndx.shndx != SHN_UNDEF)
			use_xshndx = 1;
	}
	if (use_xshndx)
		symstate_add_xshndx(argstate, symstate);
	shndx_chg = (sym->st_shndx != st_shndx);
	xshndx_chg = use_xshndx &&
	    (symstate->xshndx.data[symstate->ndx] != xshndx);


	/* If anything is going to change, issue appropiate warnings */
	if (shndx_chg || xshndx_chg) {
		/*
		 * Setting the first symbol to anything other than SHN_UNDEF
		 * produces a bad ELF file.
		 */
		if ((symstate->ndx == 0) && (shndx != SHN_UNDEF))
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_SHNDX_UNDEF0));

		/*
		 * Setting SHN_XINDEX directly, instead of providing
		 * an extended index and letting us decide to use
		 * SHN_XINDEX to implement it, is probably a mistake.
		 * Issue a warning, but go ahead and follow the directions
		 * we've been given.
		 */
		if (shndx == SHN_XINDEX)
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_SHNDX_XINDEX));

		/*
		 * If the section index can fit in the symbol, but
		 * -e is being used to force it into the extended
		 * index section, issue a warning.
		 */
		if (use_xshndx && (shndx < SHN_LORESERVE) &&
		    (st_shndx == SHN_XINDEX))
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_SHNDX_EFORCE),
			    EC_WORD(symstate->sym.sec->sec_shndx),
			    symstate->sym.sec->sec_name, EC_WORD(symstate->ndx),
			    EC_WORD(shndx));
	}

	if (shndx_chg) {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_SHNDX),
		    elfedit_shndx_to_name(argstate->obj_state,
		    sym->st_shndx),
		    elfedit_shndx_to_name(argstate->obj_state, st_shndx));
		ret = ELFEDIT_CMDRET_MOD;
		sym->st_shndx = st_shndx;
	} else {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_SHNDX),
		    elfedit_shndx_to_name(argstate->obj_state, st_shndx));
	}

	if (use_xshndx) {
		if (xshndx_chg) {
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_EXT_S_CHG),
			    symstate->xshndx.sec->sec_shndx,
			    symstate->xshndx.sec->sec_name,
			    EC_WORD(symstate->ndx),
			    elfedit_shndx_to_name(argstate->obj_state,
			    symstate->xshndx.data[symstate->ndx]),
			    elfedit_shndx_to_name(argstate->obj_state, xshndx));
			ret = ELFEDIT_CMDRET_MOD;
			symstate->xshndx.data[symstate->ndx] = xshndx;
			elfedit_modified_data(symstate->xshndx.sec);
		} else {
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_EXT_S_OK),
			    symstate->xshndx.sec->sec_shndx,
			    symstate->xshndx.sec->sec_name,
			    EC_WORD(symstate->ndx),
			    elfedit_shndx_to_name(argstate->obj_state, xshndx));
		}
	}

	return (ret);
}

static elfedit_cmdret_t
cmd_body_set_st_type(ARGSTATE *argstate, SYMSTATE *symstate)
{
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	Conv_inv_buf_t	inv_buf1, inv_buf2;
	Half		mach = argstate->obj_state->os_ehdr->e_machine;
	Sym		*sym = &symstate->sym.data[symstate->ndx];
	uchar_t		bind, type, old_type;

	/*
	 * Use the ELF_ST_TYPE() macro to access the defined bits
	 * of the st_info field related to symbol type.
	 * Accepts STT_ symbolic names as well as integers.
	 */
	bind = ELF_ST_BIND(sym->st_info);
	type = elfedit_atoconst_range(argstate->argv[1],
	    MSG_INTL(MSG_ARG_SYMBIND), 0, 15, ELFEDIT_CONST_STT);
	old_type = ELF_ST_TYPE(sym->st_info);

	if (old_type == type) {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_TYPE),
		    conv_sym_info_type(mach, type, CONV_FMT_ALT_FULLNAME,
		    &inv_buf1));
	} else {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_TYPE),
		    conv_sym_info_type(mach, old_type, CONV_FMT_ALT_FULLNAME,
		    &inv_buf1),
		    conv_sym_info_type(mach, type, CONV_FMT_ALT_FULLNAME,
		    &inv_buf2));
		ret = ELFEDIT_CMDRET_MOD;
		sym->st_info = ELF_ST_INFO(bind, type);
	}

	return (ret);
}

static elfedit_cmdret_t
cmd_body_set_st_visibility(ARGSTATE *argstate, SYMSTATE *symstate)
{
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	Conv_inv_buf_t	inv_buf1, inv_buf2;
	Sym		*sym = &symstate->sym.data[symstate->ndx];
	uchar_t		st_other = sym->st_other;
	uchar_t		vis, old_vis;

	/*
	 * Use the ELF_ST_VISIBILITY() macro to access the
	 * defined bits of the st_other field related to symbol
	 * visibility. Accepts STV_ symbolic names as well as integers.
	 */
	vis = elfedit_atoconst_range(argstate->argv[1],
	    MSG_INTL(MSG_ARG_SYMVIS), 0, STV_ELIMINATE, ELFEDIT_CONST_STV);
	old_vis = st_other & MSK_SYM_VISIBILITY;

	if (old_vis == vis) {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_VISIBILITY),
		    conv_sym_other_vis(old_vis, CONV_FMT_ALT_FULLNAME,
		    &inv_buf1));
	} else {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG),
		    symstate->sym.sec->sec_shndx, symstate->sym.sec->sec_name,
		    EC_WORD(symstate->ndx), MSG_ORIG(MSG_CMD_ST_VISIBILITY),
		    conv_sym_other_vis(old_vis, CONV_FMT_ALT_FULLNAME,
		    &inv_buf1),
		    conv_sym_other_vis(vis, CONV_FMT_ALT_FULLNAME, &inv_buf2));
		ret = ELFEDIT_CMDRET_MOD;
		st_other = (st_other & ~MSK_SYM_VISIBILITY) |
		    ELF_ST_VISIBILITY(vis);
		sym->st_other = st_other;
	}

	return (ret);
}


/*
 * Standard argument processing for sym module
 *
 * entry
 *	obj_state, argc, argv - Standard command arguments
 *	optmask - Mask of allowed optional arguments.
 *	symstate - State block for current symbol table.
 *	argstate - Address of ARGSTATE block to be initialized
 *
 * exit:
 *	On success, *argstate is initialized. On error,
 *	an error is issued and this routine does not return.
 *
 * note:
 *	Only the basic symbol table is initially referenced by
 *	argstate. Use the argstate_add_XXX() routines below to
 *	access any auxiliary sections needed.
 */
static ARGSTATE *
process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[],
    SYM_CMD_T cmd)
{
	/*
	 * We reuse this same argstate, resizing it to the required
	 * number of symbol tables on the first call, and as necessary.
	 */
	static ARGSTATE *argstate;
	static int argstate_size = 0;

	elfedit_getopt_state_t	getopt_state;
	elfedit_getopt_ret_t	*getopt_ret;
	elfedit_symtab_t	*symtab;
	int		explicit = 0;
	int		got_sym = 0;
	Word		index;
	Word		tblndx;
	size_t		size;
	SYMSTATE	*symstate;

	/* If there are no symbol tables, we can't do a thing */
	if (obj_state->os_symtabnum == 0)
		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOSYMTAB));

	/* Calulate required size of argstate and realloc as necessary */
	size = sizeof (ARGSTATE) +
	    ((obj_state->os_symtabnum - 1) * sizeof (SYMSTATE));
	if (argstate_size != size) {
		argstate = elfedit_realloc(MSG_INTL(MSG_ALLOC_ARGSTATE),
		    argstate, size);
		argstate_size = size;
	}
	bzero(argstate, argstate_size);
	argstate->obj_state = obj_state;

	elfedit_getopt_init(&getopt_state, &argc, &argv);
	while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
		argstate->optmask |= getopt_ret->gor_idmask;
		switch (getopt_ret->gor_idmask) {
		case SYM_OPT_F_SHNAME:		/* -shnam name */
			index = elfedit_name_to_shndx(obj_state,
			    getopt_ret->gor_value);
			explicit = 1;
			break;

		case SYM_OPT_F_SHNDX:		/* -shndx index */
			index = elfedit_atoui_range(getopt_ret->gor_value,
			    MSG_INTL(MSG_ARG_SECNDX), 1,
			    obj_state->os_shnum - 1, NULL);
			explicit = 1;
			break;

		case SYM_OPT_F_SHTYP:		/* -shtyp type */
			index = elfedit_type_to_shndx(obj_state,
			    elfedit_atoconst(getopt_ret->gor_value,
			    ELFEDIT_CONST_SHT));
			explicit = 1;
			break;
		}
	}

	/*
	 * Usage error if there are too many plain arguments. sym:dump accepts
	 * a single argument, while the others accept 2.
	 */
	if (((cmd == SYM_CMD_T_DUMP) && (argc > 1)) || (argc > 2))
		elfedit_command_usage();

	/*
	 * If the -symndx option was specified, the sym arg is an index
	 * into the symbol table. In this case, the symbol table must be
	 * explicitly specified (-shnam, -shndx, or -shtype).
	 */
	if ((argstate->optmask & SYM_OPT_F_SYMNDX) && !explicit)
		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NEEDEXPSYMTAB));

	/*
	 * If a section was explicitly specified, it needs
	 * be a symbol table.
	 */
	if (explicit)
		(void) elfedit_sec_issymtab(&obj_state->os_secarr[index],
		    1, NULL);

	/* If there may be an arbitrary amount of output, use a pager */
	if (argc == 0)
		elfedit_pager_init();

	/* Return the updated values of argc/argv */
	argstate->argc = argc;
	argstate->argv = argv;

	/*
	 * Decide which symbol table(s) to use. Set up the symstate
	 * array to contain them:
	 *	- If a symbol table was explicitly specified, we use
	 *		it, and only it.
	 *	- If no symbol table is explicitly specified, and the symbol
	 *		is given by name, we use all symbol tables that
	 *		contain a symbol with that name, throwing an error
	 *		if there isn't at least 1 such table.
	 *	- If no symbol table is specified, and no symbol is specified,
	 *		we use all the tables.
	 */
	symtab = obj_state->os_symtab;
	symstate = argstate->symstate;
	for (tblndx = 0; tblndx < obj_state->os_symtabnum;
	    tblndx++, symtab++) {
		/* If explicit table specified, only that table is considered */
		if (explicit && (symtab->symt_shndx != index))
			continue;

		symstate->sym.sec = elfedit_sec_getsymtab(obj_state, 1,
		    symtab->symt_shndx, NULL, &symstate->sym.data,
		    &symstate->sym.n, &symtab);
		symstate->versym.shndx = symtab->symt_versym;
		symstate->xshndx.shndx = symtab->symt_xshndx;
		if (argc > 0) {
			if (argstate->optmask & SYM_OPT_F_SYMNDX) {
				symstate->ndx = elfedit_atoui_range(
				    argstate->argv[0], MSG_INTL(MSG_ARG_SYM), 0,
				    symstate->sym.n - 1, NULL);
			} else {
				/*
				 * arg is a symbol name. Use the index of
				 * the first symbol that matches
				 */

				/*
				 * We will use debug messages for failure up
				 * until we run out of symbol tables. If we
				 * don't find a table with the desired symbol
				 * before the last table, we switch to error
				 * messages. Hence, we will jump with an error
				 * if no table will work.
				 */
				int err_type = (!got_sym &&
				    ((tblndx + 1) == obj_state->os_symtabnum)) ?
				    ELFEDIT_MSG_ERR : ELFEDIT_MSG_DEBUG;

				symstate_add_str(argstate, symstate);

				/*
				 * If the symbol table doesn't have this
				 * symbol, then forget it.
				 */
				if (elfedit_name_to_symndx(symstate->sym.sec,
				    symstate->str.sec, argstate->argv[0],
				    err_type, &symstate->ndx) == 0) {
					bzero(symstate, sizeof (*symstate));
					continue;
				}
			}
		}
		argstate->numsymstate++;
		symstate++;
		/*
		 * If the symbol table was given explicitly, and
		 * we've just taken it, then there is no reason to
		 * continue searching.
		 */
		if (explicit)
			break;
	}

	return (argstate);
}



/*
 * Called by cmd_body() to handle the value change for a single
 * symbol table.
 *
 * entry:
 *	cmd - One of the SYM_CMD_T_* constants listed above, specifying
 *		which command to implement.
 *	argstate - Overall state block
 *	symstate - State block for current symbol table.
 */
static elfedit_cmdret_t
symstate_cmd_body(SYM_CMD_T cmd, ARGSTATE *argstate, SYMSTATE *symstate)
{
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	Sym			*sym = &symstate->sym.data[symstate->ndx];

	/* You're not supposed to change the value of symbol [0] */
	if (symstate->ndx == 0)
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CHGSYMELT0),
		    EC_WORD(symstate->sym.sec->sec_shndx),
		    symstate->sym.sec->sec_name, EC_WORD(symstate->ndx));

	/* The second value is an integer giving a new value */
	switch (cmd) {
		/*
		 * SYM_CMD_T_DUMP can't get here: It never has more than
		 * one argument, and is handled above.
		 */

	case SYM_CMD_T_ST_BIND:
		ret = cmd_body_set_st_bind(argstate, symstate);
		break;

	case SYM_CMD_T_ST_INFO:
		{
			/* Treat st_info as a raw integer field */
			uchar_t st_info =
			    elfedit_atoui(argstate->argv[1], NULL);

			if (sym->st_info == st_info) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_OK),
				    symstate->sym.sec->sec_shndx,
				    symstate->sym.sec->sec_name,
				    EC_WORD(symstate->ndx),
				    MSG_ORIG(MSG_CMD_ST_INFO),
				    EC_WORD(sym->st_info));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_CHG),
				    symstate->sym.sec->sec_shndx,
				    symstate->sym.sec->sec_name,
				    EC_WORD(symstate->ndx),
				    MSG_ORIG(MSG_CMD_ST_INFO),
				    EC_WORD(sym->st_info), EC_WORD(st_info));
				ret = ELFEDIT_CMDRET_MOD;
				sym->st_info = st_info;
			}
		}
	break;

	case SYM_CMD_T_ST_NAME:
		ret = cmd_body_set_st_name(argstate, symstate);
		break;

	case SYM_CMD_T_ST_OTHER:
		{
			/* Treat st_other as a raw integer field */
			uchar_t st_other =
			    elfedit_atoui(argstate->argv[1], NULL);

			if (sym->st_other == st_other) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_OK),
				    symstate->sym.sec->sec_shndx,
				    symstate->sym.sec->sec_name,
				    EC_WORD(symstate->ndx),
				    MSG_ORIG(MSG_CMD_ST_OTHER),
				    EC_WORD(sym->st_other));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_CHG),
				    symstate->sym.sec->sec_shndx,
				    symstate->sym.sec->sec_name,
				    EC_WORD(symstate->ndx),
				    MSG_ORIG(MSG_CMD_ST_OTHER),
				    EC_WORD(sym->st_other), EC_WORD(st_other));
				ret = ELFEDIT_CMDRET_MOD;
				sym->st_other = st_other;
			}
		}
		break;

	case SYM_CMD_T_ST_SHNDX:
		ret = cmd_body_set_st_shndx(argstate, symstate);
		break;

	case SYM_CMD_T_ST_SIZE:
		{
			Xword st_size = elfedit_atoui(argstate->argv[1], NULL);

			if (sym->st_size == st_size) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    symstate->sym.sec->sec_shndx,
				    symstate->sym.sec->sec_name,
				    EC_WORD(symstate->ndx),
				    MSG_ORIG(MSG_CMD_ST_SIZE),
				    EC_XWORD(sym->st_size));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    symstate->sym.sec->sec_shndx,
				    symstate->sym.sec->sec_name,
				    EC_WORD(symstate->ndx),
				    MSG_ORIG(MSG_CMD_ST_SIZE),
				    EC_XWORD(sym->st_size), EC_XWORD(st_size));
				ret = ELFEDIT_CMDRET_MOD;
				sym->st_size = st_size;
			}
		}
		break;

	case SYM_CMD_T_ST_TYPE:
		ret = cmd_body_set_st_type(argstate, symstate);
		break;

	case SYM_CMD_T_ST_VALUE:
		{
			Addr st_value = elfedit_atoui(argstate->argv[1], NULL);

			if (sym->st_value == st_value) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    symstate->sym.sec->sec_shndx,
				    symstate->sym.sec->sec_name,
				    EC_WORD(symstate->ndx),
				    MSG_ORIG(MSG_CMD_ST_VALUE),
				    EC_ADDR(sym->st_value));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    symstate->sym.sec->sec_shndx,
				    symstate->sym.sec->sec_name,
				    EC_WORD(symstate->ndx),
				    MSG_ORIG(MSG_CMD_ST_VALUE),
				    EC_ADDR(sym->st_value),
				    EC_ADDR(st_value));
				ret = ELFEDIT_CMDRET_MOD;
				ret = ELFEDIT_CMDRET_MOD;
				sym->st_value = st_value;
			}
		}
		break;

	case SYM_CMD_T_ST_VISIBILITY:
		ret = cmd_body_set_st_visibility(argstate, symstate);
		break;
	}

	/*
	 * If we modified the symbol table, tell libelf.
	 * Any other modified sections are the responsibility
	 * of the cmd_body_set_st_*() function that did it, but
	 * everyone modifies the table itself, so we handle that here.
	 */
	if (ret == ELFEDIT_CMDRET_MOD)
		elfedit_modified_data(symstate->sym.sec);

	return (ret);
}




/*
 * Common body for the sym: module commands. These commands
 * share a large amount of common behavior, so it is convenient
 * to centralize things and use the cmd argument to handle the
 * small differences.
 *
 * entry:
 *	cmd - One of the SYM_CMD_T_* constants listed above, specifying
 *		which command to implement.
 *	obj_state, argc, argv - Standard command arguments
 */
static elfedit_cmdret_t
cmd_body(SYM_CMD_T cmd, elfedit_obj_state_t *obj_state,
    int argc, const char *argv[])
{
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	ARGSTATE		*argstate;
	SYMSTATE		*symstate;
	Word			tblndx;

	argstate = process_args(obj_state, argc, argv, cmd);

	/*
	 * If there are not 2 arguments, then this is a display request.
	 * If no arguments are present, the full table (or tables) is
	 * dumped. If there is one argument, then the specified item is shown.
	 */
	if (argstate->argc < 2) {
		print_sym(cmd, 0, argstate);
		return (ELFEDIT_CMDRET_NONE);
	}

	/*
	 * When processing multiple symbol tables, it is important that
	 * any failure happen before anything is changed. Otherwise, you
	 * can end up in a situation where things are left in an inconsistent
	 * half done state. sym:st_name has that issue when the -name_offset
	 * option is used, because the string may be insertable into some
	 * (dynstr) string tables, but not all of them. So, do the tests
	 * up front, and refuse to continue if any string insertions would
	 * fail.
	 */
	if ((cmd == SYM_CMD_T_ST_NAME) && (argstate->numsymstate > 1) &&
	    ((argstate->optmask & SYM_OPT_F_NAMOFFSET) == 0)) {
		symstate = argstate->symstate;
		for (tblndx = 0; tblndx < argstate->numsymstate;
		    tblndx++, symstate++)
			elfedit_strtab_insert_test(obj_state, symstate->str.sec,
			    NULL, argstate->argv[1]);
	}


	/* Loop over the table(s) and make the specified value change */
	symstate = argstate->symstate;
	for (tblndx = 0; tblndx < argstate->numsymstate; tblndx++, symstate++)
		if (symstate_cmd_body(cmd, argstate, symstate) ==
		    ELFEDIT_CMDRET_MOD)
			ret = ELFEDIT_CMDRET_MOD;

	/* Do autoprint */
	print_sym(cmd, 1, argstate);

	return (ret);
}




/*
 * Command completion functions for the various commands
 */

/*
 * Handle filling in the values for -shnam, -shndx, and -shtyp options.
 */
/*ARGSUSED*/
static void
cpl_sh_opt(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	enum { NAME, INDEX, TYPE }	op;
	elfedit_symtab_t		*symtab;
	Word 	tblndx;

	if ((argc != num_opt) || (argc < 2))
		return;

	if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNAM)) == 0) {
		op = NAME;
	} else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0) {
		op = INDEX;

	} else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 0) {
		op = TYPE;
		if (obj_state == NULL)	 /* No object available */
			elfedit_cpl_atoconst(cpldata,
			    ELFEDIT_CONST_SHT_ALLSYMTAB);
	} else {
		return;
	}

	if (obj_state == NULL)	 /* No object available */
		return;

	/*
	 * Loop over the symbol tables and supply command completion
	 * for the items in the file.
	 */
	symtab = obj_state->os_symtab;
	for (tblndx = 0; tblndx < obj_state->os_symtabnum;
	    tblndx++, symtab++) {
		elfedit_section_t *sec =
		    &obj_state->os_secarr[symtab->symt_shndx];

		switch (op) {
		case NAME:
			elfedit_cpl_match(cpldata, sec->sec_name, 0);
			break;
		case INDEX:
			elfedit_cpl_ndx(cpldata, symtab->symt_shndx);
			break;
		case TYPE:
			{
				elfedit_atoui_sym_t *cpl_list;

				(void) elfedit_sec_issymtab(sec, 1, &cpl_list);
				elfedit_cpl_atoui(cpldata, cpl_list);
			}
			break;
		}
	}
}

/*ARGSUSED*/
static void
cpl_st_bind(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/* Handle -shXXX options */
	cpl_sh_opt(obj_state, cpldata, argc, argv, num_opt);

	/* The second argument can be an STB_ value */
	if (argc == (num_opt + 2))
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_STB);
}

/*ARGSUSED*/
static void
cpl_st_shndx(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	elfedit_section_t *sec;
	enum { NAME, INDEX, TYPE } op;
	Word ndx;

	/* Handle -shXXX options */
	cpl_sh_opt(obj_state, cpldata, argc, argv, num_opt);

	/*
	 * The second argument can be a section name, a section
	 * index (-secshndx), or a section type (-secshtyp). We
	 * can do completions for each of these.
	 */
	if (argc != (num_opt + 2))
		return;

	op = NAME;
	for (ndx = 0; ndx < num_opt; ndx++) {
		if (strcmp(argv[ndx], MSG_ORIG(MSG_STR_MINUS_SECSHNDX)) == 0)
			op = INDEX;
		else if (strcmp(argv[ndx],
		    MSG_ORIG(MSG_STR_MINUS_SECSHTYP)) == 0)
			op = TYPE;
	}

	switch (op) {
	case NAME:
		if (obj_state == NULL)
			break;
		sec = obj_state->os_secarr;
		for (ndx = 0; ndx < obj_state->os_shnum; ndx++, sec++)
			elfedit_cpl_match(cpldata, sec->sec_name, 0);
		break;

	case INDEX:
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SHN);
		break;

	case TYPE:
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SHT);
		break;
	}
}

/*ARGSUSED*/
static void
cpl_st_type(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/* Handle -shXXX options */
	cpl_sh_opt(obj_state, cpldata, argc, argv, num_opt);

	/* The second argument can be an STT_ value */
	if (argc == (num_opt + 2))
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_STT);
}

/*ARGSUSED*/
static void
cpl_st_visibility(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/* Handle -shXXX options */
	cpl_sh_opt(obj_state, cpldata, argc, argv, num_opt);

	/* The second argument can be an STV_ value */
	if (argc == (num_opt + 2))
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_STV);
}



/*
 * Implementation functions for the commands
 */
static elfedit_cmdret_t
cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_DUMP, obj_state, argc, argv));
}


static elfedit_cmdret_t
cmd_st_bind(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_ST_BIND, obj_state, argc, argv));
}


static elfedit_cmdret_t
cmd_st_info(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_ST_INFO, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_st_name(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_ST_NAME, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_st_other(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_ST_OTHER, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_st_shndx(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_ST_SHNDX, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_st_size(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_ST_SIZE, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_st_type(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_ST_TYPE, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_st_value(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_ST_VALUE, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_st_visibility(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SYM_CMD_T_ST_VISIBILITY, obj_state, argc, argv));
}



/*ARGSUSED*/
elfedit_module_t *
elfedit_init(elfedit_module_version_t version)
{
	/* Multiple commands accept only the standard set of options */
	static elfedit_cmd_optarg_t opt_std[] = {
		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHNAME, SYM_OPT_F_SHNDX | SYM_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHNDX, SYM_OPT_F_SHNAME | SYM_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHTYP, SYM_OPT_F_SHNAME | SYM_OPT_F_SHNDX },
		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
		    /* MSG_INTL(MSG_OPTDESC_SYMNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0, SYM_OPT_F_SYMNDX },
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0 },
		{ NULL }
	};

	/* sym:dump */
	static const char *name_dump[] = {
	    MSG_ORIG(MSG_CMD_DUMP),
	    MSG_ORIG(MSG_STR_EMPTY),	/* "" makes this the default command */
	    NULL
	};
	static elfedit_cmd_optarg_t opt_dump[] = {
		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHNAME, SYM_OPT_F_SHNDX | SYM_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHNDX, SYM_OPT_F_SHNAME | SYM_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHTYP, SYM_OPT_F_SHNAME | SYM_OPT_F_SHNDX },
		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
		    /* MSG_INTL(MSG_OPTDESC_SYMNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0, SYM_OPT_F_SYMNDX },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_dump[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* sym:st_bind */
	static const char *name_st_bind[] = {
	    MSG_ORIG(MSG_CMD_ST_BIND), NULL };
	static elfedit_cmd_optarg_t arg_st_bind[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_ST_BIND) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_ST_BIND),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* sym:st_info */
	static const char *name_st_info[] = {
	    MSG_ORIG(MSG_CMD_ST_INFO), NULL };
	static elfedit_cmd_optarg_t arg_st_info[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_ST_INFO) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_ST_INFO),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* sym:st_name */
	static const char *name_st_name[] = {
	    MSG_ORIG(MSG_CMD_ST_NAME), NULL };
	static elfedit_cmd_optarg_t opt_st_name[] = {
		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHNAME, SYM_OPT_F_SHNDX | SYM_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_NAME), NULL, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHNDX, SYM_OPT_F_SHNAME | SYM_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHTYP, SYM_OPT_F_SHNAME | SYM_OPT_F_SHNDX },
		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
		    /* MSG_INTL(MSG_OPTDESC_SYMNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0,
		    SYM_OPT_F_SYMNDX, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_NAME_OFFSET),
		    /* MSG_INTL(MSG_OPTDESC_NAME_OFFSET) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_NAME_OFFSET), 0,
		    SYM_OPT_F_NAMOFFSET, 0 },
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_st_name[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_NAME),
		    /* MSG_INTL(MSG_A2_DESC_ST_NAME) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_ST_NAME),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* sym:st_other */
	static const char *name_st_other[] = {
	    MSG_ORIG(MSG_CMD_ST_OTHER), NULL };
	static elfedit_cmd_optarg_t arg_st_other[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_ST_OTHER) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_ST_OTHER),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* sym:st_shndx */
	static const char *name_st_shndx[] = {
	    MSG_ORIG(MSG_CMD_ST_SHNDX), NULL };
	static elfedit_cmd_optarg_t opt_st_shndx[] = {
		{ MSG_ORIG(MSG_STR_MINUS_E),
		    /* MSG_INTL(MSG_OPTDESC_E) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_E), 0, SYM_OPT_F_XSHINDEX, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHNAME, SYM_OPT_F_SHNDX | SYM_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_NAME), NULL, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHNDX, SYM_OPT_F_SHNAME | SYM_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
		    SYM_OPT_F_SHTYP, SYM_OPT_F_SHNAME | SYM_OPT_F_SHNDX },
		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
		    /* MSG_INTL(MSG_OPTDESC_SYMNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0,
		    SYM_OPT_F_SYMNDX, 0 },
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SECSHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SECSHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SECSHNDX),
		    0, SYM_OPT_F_SECSHNDX, SYM_OPT_F_SECSHTYP },
		{ MSG_ORIG(MSG_STR_MINUS_SECSHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SECSHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SECSHTYP),
		    0, SYM_OPT_F_SECSHTYP, SYM_OPT_F_SECSHNDX },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_st_shndx[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A2_DESC_ST_SEC) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_ST_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* sym:st_size */
	static const char *name_st_size[] = {
	    MSG_ORIG(MSG_CMD_ST_SIZE), NULL };
	static elfedit_cmd_optarg_t arg_st_size[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_ST_SIZE) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_ST_SIZE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* sym:st_type */
	static const char *name_st_type[] = {
	    MSG_ORIG(MSG_CMD_ST_TYPE), NULL };
	static elfedit_cmd_optarg_t arg_st_type[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_ST_TYPE) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_ST_TYPE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* sym:st_value */
	static const char *name_st_value[] = {
	    MSG_ORIG(MSG_CMD_ST_VALUE), NULL };
	static elfedit_cmd_optarg_t arg_st_value[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_ST_VALUE) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_ST_VALUE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* sym:st_visibility */
	static const char *name_st_visibility[] = {
	    MSG_ORIG(MSG_CMD_ST_VISIBILITY), NULL };
	static elfedit_cmd_optarg_t arg_st_visibility[] = {
		{ MSG_ORIG(MSG_STR_SYM),
		    /* MSG_INTL(MSG_A1_SYM) */
		    ELFEDIT_I18NHDL(MSG_A1_SYM),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_ST_VISIBILITY) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_ST_VISIBILITY),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	static elfedit_cmd_t cmds[] = {
		/* sym:dump */
		{ cmd_dump, cpl_sh_opt, name_dump,
		    /* MSG_INTL(MSG_DESC_DUMP) */
		    ELFEDIT_I18NHDL(MSG_DESC_DUMP),
		    /* MSG_INTL(MSG_HELP_DUMP) */
		    ELFEDIT_I18NHDL(MSG_HELP_DUMP),
		    opt_dump, arg_dump },

		/* sym:st_bind */
		{ cmd_st_bind, cpl_st_bind, name_st_bind,
		    /* MSG_INTL(MSG_DESC_ST_BIND) */
		    ELFEDIT_I18NHDL(MSG_DESC_ST_BIND),
		    /* MSG_INTL(MSG_HELP_ST_BIND) */
		    ELFEDIT_I18NHDL(MSG_HELP_ST_BIND),
		    opt_std, arg_st_bind },

		/* sym:st_info */
		{ cmd_st_info, cpl_sh_opt, name_st_info,
		    /* MSG_INTL(MSG_DESC_ST_INFO) */
		    ELFEDIT_I18NHDL(MSG_DESC_ST_INFO),
		    /* MSG_INTL(MSG_HELP_ST_INFO) */
		    ELFEDIT_I18NHDL(MSG_HELP_ST_INFO),
		    opt_std, arg_st_info },

		/* sym:st_name */
		{ cmd_st_name, cpl_sh_opt, name_st_name,
		    /* MSG_INTL(MSG_DESC_ST_NAME) */
		    ELFEDIT_I18NHDL(MSG_DESC_ST_NAME),
		    /* MSG_INTL(MSG_HELP_ST_NAME) */
		    ELFEDIT_I18NHDL(MSG_HELP_ST_NAME),
		    opt_st_name, arg_st_name },

		/* sym:st_other */
		{ cmd_st_other, cpl_sh_opt, name_st_other,
		    /* MSG_INTL(MSG_DESC_ST_OTHER) */
		    ELFEDIT_I18NHDL(MSG_DESC_ST_OTHER),
		    /* MSG_INTL(MSG_HELP_ST_OTHER) */
		    ELFEDIT_I18NHDL(MSG_HELP_ST_OTHER),
		    opt_std, arg_st_other },

		/* sym:st_shndx */
		{ cmd_st_shndx, cpl_st_shndx, name_st_shndx,
		    /* MSG_INTL(MSG_DESC_ST_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_DESC_ST_SHNDX),
		    /* MSG_INTL(MSG_HELP_ST_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_HELP_ST_SHNDX),
		    opt_st_shndx, arg_st_shndx },

		/* sym:st_size */
		{ cmd_st_size, cpl_sh_opt, name_st_size,
		    /* MSG_INTL(MSG_DESC_ST_SIZE) */
		    ELFEDIT_I18NHDL(MSG_DESC_ST_SIZE),
		    /* MSG_INTL(MSG_HELP_ST_SIZE) */
		    ELFEDIT_I18NHDL(MSG_HELP_ST_SIZE),
		    opt_std, arg_st_size },

		/* sym:st_type */
		{ cmd_st_type, cpl_st_type, name_st_type,
		    /* MSG_INTL(MSG_DESC_ST_TYPE) */
		    ELFEDIT_I18NHDL(MSG_DESC_ST_TYPE),
		    /* MSG_INTL(MSG_HELP_ST_TYPE) */
		    ELFEDIT_I18NHDL(MSG_HELP_ST_TYPE),
		    opt_std, arg_st_type },

		/* sym:st_value */
		{ cmd_st_value, cpl_sh_opt, name_st_value,
		    /* MSG_INTL(MSG_DESC_ST_VALUE) */
		    ELFEDIT_I18NHDL(MSG_DESC_ST_VALUE),
		    /* MSG_INTL(MSG_HELP_ST_VALUE) */
		    ELFEDIT_I18NHDL(MSG_HELP_ST_VALUE),
		    opt_std, arg_st_value },

		/* sym:st_visibility */
		{ cmd_st_visibility, cpl_st_visibility, name_st_visibility,
		    /* MSG_INTL(MSG_DESC_ST_VISIBILITY) */
		    ELFEDIT_I18NHDL(MSG_DESC_ST_VISIBILITY),
		    /* MSG_INTL(MSG_HELP_ST_VISIBILITY) */
		    ELFEDIT_I18NHDL(MSG_HELP_ST_VISIBILITY),
		    opt_std, arg_st_visibility },

		{ NULL }
	};

	static elfedit_module_t module = {
	    ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_NAME),
	    /* MSG_INTL(MSG_MOD_DESC) */
	    ELFEDIT_I18NHDL(MSG_MOD_DESC),
	    cmds, mod_i18nhdl_to_str };

	return (&module);
}