/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include	<stdio.h>
#include	<unistd.h>
#include	<elfedit.h>
#include	<sys/elf_SPARC.h>
#include	<sys/elf_amd64.h>
#include	<strings.h>
#include	<debug.h>
#include	<conv.h>
#include	<shdr_msg.h>




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

	SHDR_CMD_T_SH_ADDR =		1,	/* shdr:sh_addr */
	SHDR_CMD_T_SH_ADDRALIGN =	2,	/* shdr:sh_addralign */
	SHDR_CMD_T_SH_ENTSIZE =		3,	/* shdr:sh_entsize */
	SHDR_CMD_T_SH_FLAGS =		4,	/* shdr:sh_flags */
	SHDR_CMD_T_SH_INFO =		5,	/* shdr:sh_info */
	SHDR_CMD_T_SH_LINK =		6,	/* shdr:sh_link */
	SHDR_CMD_T_SH_NAME =		7,	/* shdr:sh_name */
	SHDR_CMD_T_SH_OFFSET =		8,	/* shdr:sh_offset */
	SHDR_CMD_T_SH_SIZE =		9,	/* shdr:sh_size */
	SHDR_CMD_T_SH_TYPE =		10	/* shdr:sh_type */
} SHDR_CMD_T;



#ifndef _ELF64
/*
 * We supply this function for the msg module. Only one copy is needed.
 */
const char *
_shdr_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 shdr_opt_t enum specifies a bit value for every optional
 * argument allowed by a command in this module.
 */
typedef enum {
	SHDR_OPT_F_AND =	1,	/* -and: AND (&) values to dest */
	SHDR_OPT_F_CMP =	2,	/* -cmp: Complement (~) values */
	SHDR_OPT_F_NAMOFFSET =	4,	/* -name_offset: Name arg is numeric */
					/*	 ofset rather than string */
	SHDR_OPT_F_OR =		8,	/* -or: OR (|) values to dest */
	SHDR_OPT_F_SHNDX =	16,	/* -shndx: Section by index, not name */
	SHDR_OPT_F_SHTYP =	32,	/* -shtyp: Section by type, not name */
	SHDR_OPT_F_VALUE_SHNAM = 64,	/* -value_shnam: Value of sh_info or */
					/*	sh_link given as section name */
	SHDR_OPT_F_VALUE_SHTYP = 128	/* -value_shtyp: Value of sh_info or */
					/*	sh_link given as section type */
} shdr_opt_t;


/*
 * A variable of type ARGSTATE is used by each command to maintain
 * information about the section headers and related things. It is
 * initialized by process_args(), and used by the other routines.
 */
typedef struct {
	elfedit_obj_state_t	*obj_state;
	shdr_opt_t		optmask;   	/* Mask of options used */
	int			argc;		/* # of plain arguments */
	const char		**argv;		/* Plain arguments */
} ARGSTATE;




/*
 * Standard argument processing for shdr module
 *
 * entry
 *	obj_state, argc, argv - Standard command arguments
 *	optmask - Mask of allowed optional arguments.
 *	cmd - SHDR_CMD_T_* value giving identify of caller
 *	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.
 */
static void
process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[],
    SHDR_CMD_T cmd, ARGSTATE *argstate)
{
	elfedit_getopt_state_t	getopt_state;
	elfedit_getopt_ret_t	*getopt_ret;

	bzero(argstate, sizeof (*argstate));
	argstate->obj_state = obj_state;

	elfedit_getopt_init(&getopt_state, &argc, &argv);

	/* Add each new option to the options mask */
	while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL)
		argstate->optmask |= getopt_ret->gor_idmask;

	/* Are the right number of plain arguments present? */
	switch (cmd) {
	case SHDR_CMD_T_DUMP:
		if (argc > 1)
			elfedit_command_usage();
		break;
	case SHDR_CMD_T_SH_FLAGS:
		/* shdr:sh_flags allows an arbitrary number of arguments */
		break;
	default:
		/* The remaining commands accept 2 plain arguments */
		if (argc > 2)
			elfedit_command_usage();
		break;
	}

	/* 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;
}



/*
 * Options for deciding which items print_shdr() displays.
 */
typedef enum {
	PRINT_SHDR_ALL,		/* Print all shdr[ndx:ndx+cnt-1] */
	PRINT_SHDR_TYPE,	/* Print all shdr[ndx:ndx+cnt-1] with type */
				/*	 of shdr[ndx] */
	PRINT_SHDR_NAME,	/* Print all shdr[ndx:ndx+cnt-1] with name */
				/*	 of shdr[ndx] */
} PRINT_SHDR_T;

/*
 * Print section header 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 - SHDR_CMD_T_* value giving identify of caller
 *	argstate - State block for section header array
 *	ndx - Index of first section to display
 *	cnt - Number of sections to display
 *	print_type - Specifies which items are shown
 */
static void
print_shdr(SHDR_CMD_T cmd, int autoprint, ARGSTATE *argstate,
    Word ndx, Word cnt, PRINT_SHDR_T print_type)
{
	elfedit_outstyle_t	outstyle;
	Ehdr			*ehdr = argstate->obj_state->os_ehdr;
	uchar_t			osabi = ehdr->e_ident[EI_OSABI];
	Half			mach = ehdr->e_machine;
	elfedit_section_t 	*ref_sec = &argstate->obj_state->os_secarr[ndx];


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

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

	for (; cnt--; ndx++) {
		elfedit_section_t *sec = &argstate->obj_state->os_secarr[ndx];
		Shdr *shdr = sec->sec_shdr;

		switch (print_type) {
		case PRINT_SHDR_TYPE:
			if (shdr->sh_type != ref_sec->sec_shdr->sh_type)
				continue;
			break;

		case PRINT_SHDR_NAME:
			if (strcmp(sec->sec_name, ref_sec->sec_name) != 0)
				continue;
			break;
		}

		/*
		 * If doing default output, use elfdump style where we
		 * show all section header attributes. In this case, the
		 * command that called us doesn't matter
		 */
		if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
			elfedit_printf(MSG_ORIG(MSG_STR_NL));
			elfedit_printf(MSG_INTL(MSG_ELF_SHDR), ndx,
			    sec->sec_name);
			Elf_shdr(NULL, osabi, mach, sec->sec_shdr);
			continue;
		}

		/* Non-default output is handled case by case */
		switch (cmd) {
		case SHDR_CMD_T_SH_ADDR:
			elfedit_printf(MSG_ORIG(MSG_FMT_XWORDHEXNL),
			    EC_XWORD(shdr->sh_addr));
			break;

		case SHDR_CMD_T_SH_ADDRALIGN:
			elfedit_printf(MSG_ORIG(MSG_FMT_XWORDHEXNL),
			    EC_XWORD(shdr->sh_addralign));
			break;

		case SHDR_CMD_T_SH_ENTSIZE:
			elfedit_printf(MSG_ORIG(MSG_FMT_XWORDHEXNL),
			    EC_XWORD(shdr->sh_entsize));
			break;

		case SHDR_CMD_T_SH_FLAGS:
			if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
				Conv_sec_flags_buf_t sec_flags_buf;

				elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
				    conv_sec_flags(osabi, mach, shdr->sh_flags,
				    CONV_FMT_NOBKT, &sec_flags_buf));
			} else {
				elfedit_printf(MSG_ORIG(MSG_FMT_XWORDHEXNL),
				    EC_XWORD(shdr->sh_flags));
			}
			break;

		case SHDR_CMD_T_SH_INFO:
			elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL),
			    EC_WORD(shdr->sh_info));
			break;

		case SHDR_CMD_T_SH_LINK:
			elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL),
			    EC_WORD(shdr->sh_link));
			break;

		case SHDR_CMD_T_SH_NAME:
			/*
			 * In simple output mode, we show the string. In
			 * numeric mode, we show the string table offset.
			 */
			if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
				elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
				    sec->sec_name);
			} else {
				elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL),
				    EC_WORD(shdr->sh_name));
			}
			break;

		case SHDR_CMD_T_SH_OFFSET:
			elfedit_printf(MSG_ORIG(MSG_FMT_XWORDHEXNL),
			    EC_XWORD(shdr->sh_offset));
			break;

		case SHDR_CMD_T_SH_SIZE:
			elfedit_printf(MSG_ORIG(MSG_FMT_XWORDHEXNL),
			    EC_XWORD(shdr->sh_size));
			break;

		case SHDR_CMD_T_SH_TYPE:
			if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
				Conv_inv_buf_t inv_buf;

				elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
				    conv_sec_type(osabi, mach, shdr->sh_type, 0,
				    &inv_buf));
			} else {
				elfedit_printf(MSG_ORIG(MSG_FMT_WORDHEXNL),
				    EC_WORD(shdr->sh_type));
			}
			break;
		}
	}
}


/*
 * Common body for the shdr: 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 SHDR_CMD_T_* constants listed above, specifying
 *		which command to implement.
 *	obj_state, argc, argv - Standard command arguments
 */
static elfedit_cmdret_t
cmd_body(SHDR_CMD_T cmd, elfedit_obj_state_t *obj_state,
    int argc, const char *argv[])
{
	Ehdr			*ehdr = obj_state->os_ehdr;
	uchar_t			osabi = ehdr->e_ident[EI_OSABI];
	Half			mach = ehdr->e_machine;
	ARGSTATE		argstate;
	Word			ndx;
	elfedit_section_t	*shdr_sec;
	Shdr			*shdr;
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	PRINT_SHDR_T		print_type;

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

	/* If there are no arguments, dump the whole table and return */
	if (argstate.argc == 0) {
		print_shdr(cmd, 0, &argstate, 0, obj_state->os_shnum,
		    PRINT_SHDR_ALL);
		return (ELFEDIT_CMDRET_NONE);
	}

	/*
	 * The first argument gives the section to use. This can be a
	 * name (default), section index, or section type, depending on
	 * the options used.
	 */
	if (argstate.optmask & SHDR_OPT_F_SHNDX) {
		ndx = elfedit_atoshndx(argstate.argv[0], obj_state->os_shnum);
		print_type = PRINT_SHDR_ALL;
	} else if (argstate.optmask & SHDR_OPT_F_SHTYP) {
		ndx = elfedit_type_to_shndx(obj_state,
		    elfedit_atoconst(argstate.argv[0], ELFEDIT_CONST_SHT));
		print_type = PRINT_SHDR_TYPE;
	} else {
		ndx = elfedit_name_to_shndx(obj_state, argstate.argv[0]);
		print_type = PRINT_SHDR_NAME;
	}

	/* If there is a single argument, display that item and return */
	if (argstate.argc == 1) {
		Word	cnt;

		cnt = (print_type == PRINT_SHDR_ALL) ?
		    1 : obj_state->os_shnum - ndx;
		print_shdr(cmd, 0, &argstate, ndx, cnt, print_type);
		return (ELFEDIT_CMDRET_NONE);
	}

	/*
	 * Section [0] is supposed to be all zero unless extended sections
	 * are in force. Rather than setting extended values directly,
	 * it is expected to be handled by libelf. So, a direct change here
	 * is probably not what was intended.
	 */
	if (ndx == 0)
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CHGSHDR0));

	/* The second value is an integer giving a new value */
	shdr_sec = &obj_state->os_secarr[ndx];
	shdr = shdr_sec->sec_shdr;
	switch (cmd) {
		/*
		 * SHDR_CMD_T_DUMP can't get here: It never has more than
		 * one argument, and is handled above.
		 */

	case SHDR_CMD_T_SH_ADDR:
		{
			Addr sh_addr = elfedit_atoui(argstate.argv[1], NULL);

			if (shdr->sh_addr == sh_addr) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_ADDR),
				    EC_ADDR(shdr->sh_addr));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_ADDR),
				    EC_ADDR(shdr->sh_addr), EC_ADDR(sh_addr));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_addr = sh_addr;
			}
		}
		break;

	case SHDR_CMD_T_SH_ADDRALIGN:
		{
			Xword	sh_addralign;

			sh_addralign = elfedit_atoui(argstate.argv[1], NULL);
			if (elfedit_bits_set(sh_addralign,
			    sizeof (sh_addralign)) > 1)
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_ADDRALIGN),
				    argstate.argv[1]);
			if (shdr->sh_addralign == sh_addralign) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_ADDRALIGN),
				    EC_XWORD(shdr->sh_addralign));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_ADDRALIGN),
				    EC_XWORD(shdr->sh_addralign),
				    EC_XWORD(sh_addralign));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_addralign = sh_addralign;
			}
		}
		break;

	case SHDR_CMD_T_SH_ENTSIZE:
		{
			Xword sh_entsize;

			sh_entsize = elfedit_atoui(argstate.argv[1], NULL);
			if (shdr->sh_entsize == sh_entsize) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_ENTSIZE),
				    EC_XWORD(shdr->sh_entsize));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_ENTSIZE),
				    EC_XWORD(shdr->sh_entsize),
				    EC_XWORD(sh_entsize));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_entsize = sh_entsize;
			}
		}
		break;

	case SHDR_CMD_T_SH_FLAGS:
		{
			Conv_sec_flags_buf_t buf1, buf2;
			Word	sh_flags = 0;
			int	i;

						/* Collect the flag arguments */
			for (i = 1; i < argstate.argc; i++)
				sh_flags |=
				    (Word) elfedit_atoconst(argstate.argv[i],
				    ELFEDIT_CONST_SHF);

			/* Complement the value? */
			if (argstate.optmask & SHDR_OPT_F_CMP)
				sh_flags = ~sh_flags;

			/* Perform any requested bit operations */
			if (argstate.optmask & SHDR_OPT_F_AND)
				sh_flags &= shdr->sh_flags;
			else if (argstate.optmask & SHDR_OPT_F_OR)
				sh_flags |= shdr->sh_flags;

			/* Set the value */
			if (shdr->sh_flags == sh_flags) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_S_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_FLAGS),
				    conv_sec_flags(osabi, mach,
				    shdr->sh_flags, 0, &buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_S_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_FLAGS),
				    conv_sec_flags(osabi, mach,
				    shdr->sh_flags, 0, &buf1),
				    conv_sec_flags(osabi, mach,
				    sh_flags, 0, &buf2));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_flags = sh_flags;
			}
		}
		break;

	case SHDR_CMD_T_SH_INFO:
		{
			Word sh_info;

			if (argstate.optmask & SHDR_OPT_F_VALUE_SHNAM)
				sh_info = elfedit_name_to_shndx(obj_state,
				    argstate.argv[1]);
			else if (argstate.optmask & SHDR_OPT_F_VALUE_SHTYP)
				sh_info = elfedit_type_to_shndx(obj_state,
				    elfedit_atoconst(argstate.argv[1],
				    ELFEDIT_CONST_SHT));
			else
				sh_info = elfedit_atoui(argstate.argv[1], NULL);

			if (shdr->sh_info == sh_info) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_INFO),
				    EC_WORD(shdr->sh_info));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_INFO),
				    EC_WORD(shdr->sh_info), EC_WORD(sh_info));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_info = sh_info;
			}
		}
		break;

	case SHDR_CMD_T_SH_LINK:
		{
			Word sh_link;

			if (argstate.optmask & SHDR_OPT_F_VALUE_SHNAM)
				sh_link = elfedit_name_to_shndx(obj_state,
				    argstate.argv[1]);
			else if (argstate.optmask & SHDR_OPT_F_VALUE_SHTYP)
				sh_link = elfedit_type_to_shndx(obj_state,
				    elfedit_atoconst(argstate.argv[1],
				    ELFEDIT_CONST_SHT));
			else
				sh_link = elfedit_atoui(argstate.argv[1], NULL);

			if (shdr->sh_link == sh_link) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_LINK),
				    EC_WORD(shdr->sh_link));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_LINK),
				    EC_WORD(shdr->sh_link), EC_WORD(sh_link));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_link = sh_link;
			}
		}
		break;

	case SHDR_CMD_T_SH_NAME:
		{
			elfedit_section_t *shstr_sec =
			    &obj_state->os_secarr[obj_state->os_shstrndx];
			Word sh_name;

			/*
			 * If -name_offset was specified, this is an offset
			 * into the string table. Otherwise it is a string
			 * we need to turn into an offset.
			 */
			sh_name = (argstate.optmask & SHDR_OPT_F_NAMOFFSET) ?
			    elfedit_atoui(argstate.argv[1], NULL) :
			    elfedit_strtab_insert(obj_state,
			    shstr_sec, NULL, argstate.argv[1]);
			if (shdr->sh_name == sh_name) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_NAME),
				    EC_WORD(shdr->sh_name));
			} else {
				/*
				 * The section name is cached, so we must
				 * also update that value. This call will
				 * warn if the offset is out of range, and
				 * will supply a safe string in that case.
				 */
				shdr_sec->sec_name =
				    elfedit_offset_to_str(shstr_sec,
				    sh_name, ELFEDIT_MSG_DEBUG, 1);

				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_D_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_NAME),
				    EC_WORD(shdr->sh_name), EC_WORD(sh_name));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_name = sh_name;
			}
		}
		break;

	case SHDR_CMD_T_SH_OFFSET:
		{
			Off sh_offset;

			sh_offset = elfedit_atoui(argstate.argv[1], NULL);
			if (shdr->sh_offset == sh_offset) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_OFFSET),
				    EC_XWORD(shdr->sh_offset));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_OFFSET),
				    EC_XWORD(shdr->sh_offset),
				    EC_XWORD(sh_offset));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_offset = sh_offset;
			}
		}
		break;

	case SHDR_CMD_T_SH_SIZE:
		{
			Xword sh_size;

			sh_size = elfedit_atoui(argstate.argv[1], NULL);
			if (shdr->sh_size == sh_size) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_SIZE),
				    EC_XWORD(shdr->sh_size));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_SIZE),
				    EC_XWORD(shdr->sh_size),
				    EC_XWORD(sh_size));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_size = sh_size;
			}
		}
		break;

	case SHDR_CMD_T_SH_TYPE:
		{
			Word sh_type = elfedit_atoconst(argstate.argv[1],
			    ELFEDIT_CONST_SHT);
			Conv_inv_buf_t inv_buf1, inv_buf2;

			if (shdr->sh_type == sh_type) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_S_OK),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_TYPE),
				    conv_sec_type(osabi, mach, shdr->sh_type,
				    0, &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_S_CHG),
				    ndx, shdr_sec->sec_name,
				    MSG_ORIG(MSG_CMD_SH_TYPE),
				    conv_sec_type(osabi, mach, shdr->sh_type,
				    0, &inv_buf1),
				    conv_sec_type(osabi, mach, sh_type,
				    0, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD;
				shdr->sh_type = sh_type;
			}
		}
		break;
	}

	/*
	 * If we modified the section header array, tell libelf.
	 */
	if (ret == ELFEDIT_CMDRET_MOD)
		elfedit_modified_shdr(shdr_sec);

	/* Do autoprint */
	print_shdr(cmd, 1, &argstate, ndx, 1, PRINT_SHDR_ALL);

	return (ret);
}




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

/*
 * All of the commands accept the same first argument (sec) that
 * specifies the section. This argument can be a section name
 * (default), section index, or section type, depending on the
 * options used. This routine determines which case is current,
 * and then supplies completion for the first argument.
 */
static void
cpl_1starg_sec(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;

	if (argc != (num_opt + 1))
		return;

	op = NAME;
	for (ndx = 0; ndx < num_opt; ndx++) {
		if (strcmp(argv[ndx], MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0)
			op = INDEX;
		else if (strcmp(argv[ndx], MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 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_sh_flags(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/* Handle -shXXX options */
	cpl_1starg_sec(obj_state, cpldata, argc, argv, num_opt);

	/* The second and following arguments can be an SHF_ value */
	if (argc >= (num_opt + 2))
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SHF);
}

/*
 * For shdr:sh_info and shdr:sh_link: The value argument can be an
 * integer, section name, or section type.
 */
/*ARGSUSED*/
static void
cpl_sh_infolink(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	elfedit_section_t *sec;
	enum { NAME, INTVAL, TYPE } op;
	Word ndx;

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

	if (argc != (num_opt + 2))
		return;

	op = INTVAL;
	for (ndx = 0; ndx < num_opt; ndx++) {
		if (strcmp(argv[ndx], MSG_ORIG(MSG_STR_MINUS_VALUE_SHNAM)) == 0)
			op = NAME;
		else if (strcmp(argv[ndx],
		    MSG_ORIG(MSG_STR_MINUS_VALUE_SHTYP)) == 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 TYPE:
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SHT);
		break;
	}
}

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

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



/*
 * 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(SHDR_CMD_T_DUMP, obj_state, argc, argv));
}


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


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


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

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

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

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

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

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

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

static elfedit_cmdret_t
cmd_sh_type(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
	return (cmd_body(SHDR_CMD_T_SH_TYPE, 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[] = {
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), 0,
		    SHDR_OPT_F_SHNDX, SHDR_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), 0,
		    SHDR_OPT_F_SHTYP, SHDR_OPT_F_SHNDX },
		{ NULL }
	};

	/*
	 * sh_info and sh_link accept the standard options above,
	 * plus -value_shnam and -value_shtyp.
	 */
	static elfedit_cmd_optarg_t opt_infolink[] = {
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), 0,
		    SHDR_OPT_F_SHNDX, SHDR_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), 0,
		    SHDR_OPT_F_SHTYP, SHDR_OPT_F_SHNDX },
		{ MSG_ORIG(MSG_STR_MINUS_VALUE_SHNAM),
		    /* MSG_INTL(MSG_OPTDESC_VALUE_SHNAM) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_VALUE_SHNAM), 0,
		    SHDR_OPT_F_VALUE_SHNAM, SHDR_OPT_F_VALUE_SHNAM },
		{ MSG_ORIG(MSG_STR_MINUS_VALUE_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_VALUE_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_VALUE_SHTYP), 0,
		    SHDR_OPT_F_VALUE_SHTYP, SHDR_OPT_F_VALUE_SHTYP },
		{ NULL }
	};

	/* shdr:sh_addr */
	static const char *name_sh_addr[] = {
	    MSG_ORIG(MSG_CMD_SH_ADDR), NULL };
	static elfedit_cmd_optarg_t arg_sh_addr[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_SH_ADDR) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_ADDR),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr: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_SHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), 0,
		    SHDR_OPT_F_SHNDX, SHDR_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), 0,
		    SHDR_OPT_F_SHTYP, SHDR_OPT_F_SHNDX },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_dump[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr:sh_addralign */
	static const char *name_sh_addralign[] = {
	    MSG_ORIG(MSG_CMD_SH_ADDRALIGN), NULL };
	static elfedit_cmd_optarg_t arg_sh_addralign[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_SH_ADDRALIGN) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_ADDRALIGN),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr:sh_entsize */
	static const char *name_sh_entsize[] = {
	    MSG_ORIG(MSG_CMD_SH_ENTSIZE), NULL };
	static elfedit_cmd_optarg_t arg_sh_entsize[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_SH_ENTSIZE) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_ENTSIZE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr:sh_flags */
	static const char *name_sh_flags[] = {
	    MSG_ORIG(MSG_CMD_SH_FLAGS), NULL };
	static elfedit_cmd_optarg_t opt_sh_flags[] = {
		{ ELFEDIT_STDOA_OPT_AND, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, SHDR_OPT_F_AND, SHDR_OPT_F_OR },
		{ ELFEDIT_STDOA_OPT_CMP, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, SHDR_OPT_F_CMP, 0 },
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ ELFEDIT_STDOA_OPT_OR, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, SHDR_OPT_F_OR, SHDR_OPT_F_AND },
		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), 0,
		    SHDR_OPT_F_SHNDX, SHDR_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), 0,
		    SHDR_OPT_F_SHTYP, SHDR_OPT_F_SHNDX },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_sh_flags[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_SH_FLAGS) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_FLAGS),
		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
		{ NULL }
	};

	/* shdr:sh_info */
	static const char *name_sh_info[] = {
	    MSG_ORIG(MSG_CMD_SH_INFO), NULL };
	static elfedit_cmd_optarg_t arg_sh_info[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_SH_INFO) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_INFO),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr:sh_link */
	static const char *name_sh_link[] = {
	    MSG_ORIG(MSG_CMD_SH_LINK), NULL };
	static elfedit_cmd_optarg_t arg_sh_link[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_SH_LINK) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_LINK),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr:sh_name */
	static const char *name_sh_name[] = {
	    MSG_ORIG(MSG_CMD_SH_NAME), NULL };
	static elfedit_cmd_optarg_t opt_sh_name[] = {
		{ MSG_ORIG(MSG_STR_MINUS_NAME_OFFSET),
		    /* MSG_INTL(MSG_OPTDESC_NAME_OFFSET) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_NAME_OFFSET), 0,
		    SHDR_OPT_F_NAMOFFSET, 0 },
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), 0,
		    SHDR_OPT_F_SHNDX, SHDR_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), 0,
		    SHDR_OPT_F_SHTYP, SHDR_OPT_F_SHNDX },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_sh_name[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_NAME),
		    /* MSG_INTL(MSG_A2_DESC_SH_NAME) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_NAME),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr:sh_offset */
	static const char *name_sh_offset[] = {
	    MSG_ORIG(MSG_CMD_SH_OFFSET), NULL };
	static elfedit_cmd_optarg_t arg_sh_offset[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_SH_OFFSET) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_OFFSET),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr:sh_size */
	static const char *name_sh_size[] = {
	    MSG_ORIG(MSG_CMD_SH_SIZE), NULL };
	static elfedit_cmd_optarg_t arg_sh_size[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_SH_SIZE) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_SIZE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr:sh_type */
	static const char *name_sh_type[] = {
	    MSG_ORIG(MSG_CMD_SH_TYPE), NULL };
	static elfedit_cmd_optarg_t arg_sh_type[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_A1_SEC) */
		    ELFEDIT_I18NHDL(MSG_A1_SEC),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_DESC_SH_TYPE) */
		    ELFEDIT_I18NHDL(MSG_A2_DESC_SH_TYPE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	static elfedit_cmd_t cmds[] = {
		/* shdr:dump */
		{ cmd_dump, cpl_1starg_sec, 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 },

		/* shdr:sh_addr */
		{ cmd_sh_addr, cpl_1starg_sec, name_sh_addr,
		    /* MSG_INTL(MSG_DESC_SH_ADDR) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_ADDR),
		    /* MSG_INTL(MSG_HELP_SH_ADDR) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_ADDR),
		    opt_std, arg_sh_addr },

		/* shdr:sh_addralign */
		{ cmd_sh_addralign, cpl_1starg_sec, name_sh_addralign,
		    /* MSG_INTL(MSG_DESC_SH_ADDRALIGN) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_ADDRALIGN),
		    /* MSG_INTL(MSG_HELP_SH_ADDRALIGN) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_ADDRALIGN),
		    opt_std, arg_sh_addralign },

		/* shdr:sh_entsize */
		{ cmd_sh_entsize, cpl_1starg_sec, name_sh_entsize,
		    /* MSG_INTL(MSG_DESC_SH_ENTSIZE) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_ENTSIZE),
		    /* MSG_INTL(MSG_HELP_SH_ENTSIZE) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_ENTSIZE),
		    opt_std, arg_sh_entsize },

		/* shdr:sh_flags */
		{ cmd_sh_flags, cpl_sh_flags, name_sh_flags,
		    /* MSG_INTL(MSG_DESC_SH_FLAGS) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_FLAGS),
		    /* MSG_INTL(MSG_HELP_SH_FLAGS) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_FLAGS),
		    opt_sh_flags, arg_sh_flags },

		/* shdr:sh_info */
		{ cmd_sh_info, cpl_sh_infolink, name_sh_info,
		    /* MSG_INTL(MSG_DESC_SH_INFO) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_INFO),
		    /* MSG_INTL(MSG_HELP_SH_INFO) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_INFO),
		    opt_infolink, arg_sh_info },

		/* shdr:sh_link */
		{ cmd_sh_link, cpl_sh_infolink, name_sh_link,
		    /* MSG_INTL(MSG_DESC_SH_LINK) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_LINK),
		    /* MSG_INTL(MSG_HELP_SH_LINK) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_LINK),
		    opt_infolink, arg_sh_link },

		/* shdr:sh_name */
		{ cmd_sh_name, cpl_1starg_sec, name_sh_name,
		    /* MSG_INTL(MSG_DESC_SH_NAME) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_NAME),
		    /* MSG_INTL(MSG_HELP_SH_NAME) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_NAME),
		    opt_sh_name, arg_sh_name },

		/* shdr:sh_offset */
		{ cmd_sh_offset, cpl_1starg_sec, name_sh_offset,
		    /* MSG_INTL(MSG_DESC_SH_OFFSET) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_OFFSET),
		    /* MSG_INTL(MSG_HELP_SH_OFFSET) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_OFFSET),
		    opt_std, arg_sh_offset },

		/* shdr:sh_size */
		{ cmd_sh_size, cpl_1starg_sec, name_sh_size,
		    /* MSG_INTL(MSG_DESC_SH_SIZE) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_SIZE),
		    /* MSG_INTL(MSG_HELP_SH_SIZE) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_SIZE),
		    opt_std, arg_sh_size },

		/* shdr:sh_type */
		{ cmd_sh_type, cpl_sh_type, name_sh_type,
		    /* MSG_INTL(MSG_DESC_SH_TYPE) */
		    ELFEDIT_I18NHDL(MSG_DESC_SH_TYPE),
		    /* MSG_INTL(MSG_HELP_SH_TYPE) */
		    ELFEDIT_I18NHDL(MSG_HELP_SH_TYPE),
		    opt_std, arg_sh_type },

		{ 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);
}