/*
 * 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	<ctype.h>
#include	<elfedit.h>
#include	<sys/elf_SPARC.h>
#include	<sys/elf_amd64.h>
#include	<strings.h>
#include	<conv.h>
#include	<debug.h>
#include	<ehdr_msg.h>




/*
 * This module handles changes to the ELF header
 */



/*
 * This module uses shared code for several of the commands.
 * It is sometimes necessary to know which specific command
 * is active.
 */
typedef enum {
	/* Dump command, used as module default to display ELF header */
	EHDR_CMD_T_DUMP =		0,	/* ehdr:dump */

	/* Commands that correspond directly to ELF header fields */
	EHDR_CMD_T_E_IDENT =		1,	/* ehdr:e_ident */
	EHDR_CMD_T_E_TYPE =		2,	/* ehdr:e_type */
	EHDR_CMD_T_E_MACHINE =		3,	/* ehdr:e_machine */
	EHDR_CMD_T_E_VERSION =		4,	/* ehdr:e_version */
	EHDR_CMD_T_E_ENTRY =		5,	/* ehdr:e_entry */
	EHDR_CMD_T_E_PHOFF =		6,	/* ehdr:e_phoff */
	EHDR_CMD_T_E_SHOFF =		7,	/* ehdr:e_shoff */
	EHDR_CMD_T_E_FLAGS =		8,	/* ehdr:e_flags */
	EHDR_CMD_T_E_EHSIZE =		9,	/* ehdr:e_ehsize */
	EHDR_CMD_T_E_PHENTSIZE =	10,	/* ehdr:e_phentsize */
	EHDR_CMD_T_E_PHNUM =		11,	/* ehdr:e_phnum */
	EHDR_CMD_T_E_SHENTSIZE =	12,	/* ehdr:e_shentsize */
	EHDR_CMD_T_E_SHNUM =		13,	/* ehdr:e_shnum */
	EHDR_CMD_T_E_SHSTRNDX =		14,	/* ehdr:e_shstrndx */

	/* Commands that correspond to the e_ident[] array in ELF hdr */
	EHDR_CMD_T_EI_MAG0 =		15,	/* ehdr:ei_mag0 */
	EHDR_CMD_T_EI_MAG1 =		16,	/* ehdr:ei_mag1 */
	EHDR_CMD_T_EI_MAG2 =		17,	/* ehdr:ei_mag2 */
	EHDR_CMD_T_EI_MAG3 =		18,	/* ehdr:ei_mag3 */
	EHDR_CMD_T_EI_CLASS =		19,	/* ehdr:ei_class */
	EHDR_CMD_T_EI_DATA =		20,	/* ehdr:ei_data */
	EHDR_CMD_T_EI_VERSION =		21,	/* ehdr:ei_version */
	EHDR_CMD_T_EI_OSABI =		22,	/* ehdr:ei_osabi */
	EHDR_CMD_T_EI_ABIVERSION =	23	/* ehdr:ei_abiversion */
} EHDR_CMD_T;






#ifndef _ELF64
/*
 * We supply this function for the msg module
 */
const char *
_ehdr_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 ehdr_opt_t enum specifies a bit value for every optional
 * argument allowed by a command in this module.
 */
typedef enum {
	EHDR_OPT_F_AND =	1,	/* -and: AND (&) values to dest */
	EHDR_OPT_F_CMP =	2,	/* -cmp: Complement (~) values */
	EHDR_OPT_F_OR =		4,	/* -or: OR (|) values to dest */
	EHDR_OPT_F_SHNDX =	8,	/* -shndx: sec argument is index of */
					/*	section, not name */
	EHDR_OPT_F_SHTYP =	16	/* -shtyp: sec argument is type of */
					/*	section, not name */
} ehdr_opt_t;


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



/*
 * Standard argument processing for ehdr module
 *
 * entry
 *	obj_state, argc, argv - Standard command arguments
 *	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[],
    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;

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






/*
 * Format the given magic number byte into a buffer
 *
 * entry:
 *	value - Value of the magic value byte given by
 *		ehdr->ei_ident[EI_MAG?]
 */
static const char *
conv_magic_value(int value)
{
	/*
	 * This routine can be called twice within a single C statement,
	 * so we use alternating buffers on each call to allow this
	 * without requiring the caller to supply a buffer (the size of
	 * which they don't know).
	 */
	static char buf1[20];
	static char buf2[20];
	static char *buf;

	/* Switch buffers */
	buf = (buf == buf1) ? buf2 : buf1;

	if (isprint(value))
		(void) snprintf(buf, sizeof (buf1),
		    MSG_ORIG(MSG_FMT_HEXNUM_QCHR), value, value);
	else
		(void) snprintf(buf, sizeof (buf1),
		    MSG_ORIG(MSG_FMT_HEXNUM), value);
	return (buf);
}



/*
 * Print ELF header values, taking the calling command, and output style
 * into account.
 *
 * entry:
 *	cmd - EHDR_CMD_T_* value giving identify of caller
 *	e_ident_ndx - Ignored unless cmd is EHDR_CMD_T_E_IDENT. In IDENT
 *		case, index of item in e_ident[] array to display, or
 *		-1 to display the entire array.
 *	autoprint - If True, output is only produced if the elfedit
 *		autoprint flag is set. If False, output is always produced.
 *	argstate - Argument state block
 */
static void
print_ehdr(EHDR_CMD_T cmd, int e_ident_ndx, int autoprint,
    ARGSTATE *argstate)
{
	elfedit_outstyle_t	outstyle;
	Conv_fmt_flags_t	flags_fmt_flags = 0;
	Ehdr		*ehdr;
	int		c;
	Conv_inv_buf_t	inv_buf;

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

	/*
	 * Pick an output style. ehdr:dump is required to use the default
	 * style. The other commands use the current output style.
	 */
	if (cmd == EHDR_CMD_T_DUMP) {
		outstyle = ELFEDIT_OUTSTYLE_DEFAULT;
	} else {
		outstyle = elfedit_outstyle();

		/*
		 * When the caller specifies the simple output style,
		 * omit the brackets from around the values.
		 */
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE)
			flags_fmt_flags = CONV_FMT_NOBKT;

		/*
		 * For things that show a single header item, switch
		 * from default to simple mode.
		 */
		if ((outstyle == ELFEDIT_OUTSTYLE_DEFAULT) &&
		    ((cmd != EHDR_CMD_T_E_IDENT) || (e_ident_ndx != -1)))
			outstyle = ELFEDIT_OUTSTYLE_SIMPLE;
	}

	ehdr = argstate->obj_state->os_ehdr;

	/*
	 * If doing default output, use elfdump style where we
	 * show the full ELF header. In this case, the command
	 * that called us doesn't matter. This can only happen
	 * from ehdr:dump or ehdr:e_ident/
	 */
	if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
		const char *ndx, *value;
		char ndx_buf[64], value_buf[20];
		int i;

		if (cmd == EHDR_CMD_T_DUMP) {
			Elf_ehdr(NULL, ehdr,
			    argstate->obj_state->os_secarr[0].sec_shdr);
			elfedit_printf(MSG_ORIG(MSG_STR_NL));
		}

		/*
		 * Elf_ehdr() does not display all of e_ident[], so we
		 * augment by displaying the entire array separately.
		 */
		elfedit_printf(MSG_ORIG(MSG_STR_EIDENT_HDR));

		for (i = 0; i < EI_NIDENT; i++) {
			ndx = value = NULL;

			switch (i) {
			case EI_MAG0:
			case EI_MAG1:
			case EI_MAG2:
			case EI_MAG3:
				ndx = elfedit_atoconst_value_to_str(
				    ELFEDIT_CONST_EI, i, 1);
				value = conv_magic_value(ehdr->e_ident[i]);
				break;
			case EI_CLASS:
				ndx = elfedit_atoconst_value_to_str(
				    ELFEDIT_CONST_EI, EI_CLASS, 1);
				value = conv_ehdr_class(ehdr->e_ident[EI_CLASS],
				    0, &inv_buf);
				break;
			case EI_DATA:
				ndx = elfedit_atoconst_value_to_str(
				    ELFEDIT_CONST_EI, EI_DATA, 1);
				value = conv_ehdr_data(ehdr->e_ident[EI_DATA],
				    0, &inv_buf);
				break;
			case EI_VERSION:
				ndx = elfedit_atoconst_value_to_str(
				    ELFEDIT_CONST_EI, EI_VERSION, 1);
				value = conv_ehdr_vers(
				    ehdr->e_ident[EI_VERSION], 0, &inv_buf);
				break;
			case EI_OSABI:
				ndx = elfedit_atoconst_value_to_str(
				    ELFEDIT_CONST_EI, EI_OSABI, 1);
				value = conv_ehdr_osabi(ehdr->e_ident[EI_OSABI],
				    0, &inv_buf);
				break;
			case EI_ABIVERSION:
				ndx = elfedit_atoconst_value_to_str(
				    ELFEDIT_CONST_EI, EI_ABIVERSION, 1);
				value = conv_ehdr_abivers(
				    ehdr->e_ident[EI_OSABI],
				    ehdr->e_ident[EI_ABIVERSION],
				    CONV_FMT_DECIMAL, &inv_buf);
				break;
			default:
				value = value_buf;
				(void) snprintf(value_buf, sizeof (value_buf),
				    MSG_ORIG(MSG_FMT_HEXNUM), ehdr->e_ident[i]);
				break;
			}

			if (ndx == NULL)
				(void) snprintf(ndx_buf, sizeof (ndx_buf),
				    MSG_ORIG(MSG_FMT_BKTINT), i);
			else
				(void) snprintf(ndx_buf, sizeof (ndx_buf),
				    MSG_ORIG(MSG_FMT_BKTSTR), ndx);
			elfedit_printf(MSG_ORIG(MSG_FMT_EI_ELT),
			    ndx_buf, value);
		}
		return;
	}


	switch (cmd) {
	case EHDR_CMD_T_E_IDENT:
		{
			int		i, cnt;

			/* Show one element, or the entire thing? */
			if (e_ident_ndx == -1) {
				i = 0;
				cnt = EI_NIDENT;
			} else {
				i = e_ident_ndx;
				cnt = 1;
			}

			for (; cnt-- > 0; i++) {
				/*
				 * If using numeric style, or there is
				 * no conversion routine for this item,
				 * print a simple hex value.
				 */
				if ((outstyle == ELFEDIT_OUTSTYLE_NUM) ||
				    (i > EI_ABIVERSION)) {
					elfedit_printf(
					    MSG_ORIG(MSG_FMT_HEXNUMNL),
					    ehdr->e_ident[i]);
					continue;
				}

				/* Handle special cases in simple mode */
				switch (i) {
				case EI_MAG0:
				case EI_MAG1:
				case EI_MAG2:
				case EI_MAG3:
					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
					    conv_magic_value(ehdr->e_ident[i]));
					continue;
				case EI_CLASS:
					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
					    conv_ehdr_class(
					    ehdr->e_ident[EI_CLASS], 0,
					    &inv_buf));
					continue;
				case EI_DATA:
					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
					    conv_ehdr_data(
					    ehdr->e_ident[EI_DATA], 0,
					    &inv_buf));
					continue;
				case EI_VERSION:
					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
					    conv_ehdr_vers(
					    ehdr->e_ident[EI_VERSION], 0,
					    &inv_buf));
					continue;
				case EI_OSABI:
					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
					    conv_ehdr_osabi(
					    ehdr->e_ident[EI_OSABI], 0,
					    &inv_buf));
					continue;
				case EI_ABIVERSION:
					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
					    conv_ehdr_abivers(
					    ehdr->e_ident[EI_OSABI],
					    ehdr->e_ident[EI_ABIVERSION],
					    CONV_FMT_DECIMAL, &inv_buf));
					continue;
				}
			}
		}
		return;

	case EHDR_CMD_T_E_TYPE:
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE)
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_ehdr_type(ehdr->e_ident[EI_OSABI],
			    ehdr->e_type, 0, &inv_buf));
		else
			elfedit_printf(MSG_ORIG(MSG_FMT_DECNUMNL),
			    ehdr->e_type);
		return;

	case EHDR_CMD_T_E_MACHINE:
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_ehdr_mach(ehdr->e_machine, 0, &inv_buf));
		} else {
			elfedit_printf(MSG_ORIG(MSG_FMT_DECNUMNL),
			    EC_WORD(ehdr->e_machine));
		}
		return;

	case EHDR_CMD_T_E_VERSION:
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE)
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_ehdr_vers(ehdr->e_version, 0, &inv_buf));
		else
			elfedit_printf(MSG_ORIG(MSG_FMT_DECNUMNL),
			    ehdr->e_version);
		return;

	case EHDR_CMD_T_E_ENTRY:
		elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL),
		    EC_WORD(ehdr->e_entry));
		return;

	case EHDR_CMD_T_E_PHOFF:
		elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL),
		    EC_WORD(ehdr->e_phoff));
		return;

	case EHDR_CMD_T_E_SHOFF:
		elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL),
		    EC_WORD(ehdr->e_shoff));
		return;

	case EHDR_CMD_T_E_FLAGS:
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
			Conv_ehdr_flags_buf_t	flags_buf;

			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_ehdr_flags(ehdr->e_machine, ehdr->e_flags,
			    flags_fmt_flags, &flags_buf));
		} else {
			elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL),
			    ehdr->e_flags);
		}
		return;

	case EHDR_CMD_T_E_EHSIZE:
		elfedit_printf(MSG_ORIG(MSG_FMT_DECNUMNL),
		    EC_WORD(ehdr->e_ehsize));
		return;

	case EHDR_CMD_T_E_PHENTSIZE:
		elfedit_printf(MSG_ORIG(MSG_FMT_DECNUMNL),
		    EC_WORD(ehdr->e_phentsize));
		return;

	case EHDR_CMD_T_E_PHNUM:
		{
			Word num = ehdr->e_phnum;

			/*
			 * If using extended indexes, fetch the real
			 * value from shdr[0].sh_info
			 */
			if (num == PN_XNUM)
				num = argstate->obj_state->
				    os_secarr[0].sec_shdr->sh_info;

			elfedit_printf(MSG_ORIG(MSG_FMT_DECNUMNL),
			    EC_WORD(num));
		}
		return;

	case EHDR_CMD_T_E_SHENTSIZE:
		elfedit_printf(MSG_ORIG(MSG_FMT_DECNUMNL),
		    EC_WORD(ehdr->e_shentsize));
		return;

	case EHDR_CMD_T_E_SHNUM:
		{
			Word num = ehdr->e_shnum;

			/*
			 * If using extended indexes, fetch the real
			 * value from shdr[0].sh_size
			 */
			if (num == 0)
				num = argstate->obj_state->
				    os_secarr[0].sec_shdr->sh_size;

			elfedit_printf(MSG_ORIG(MSG_FMT_DECNUMNL),
			    EC_WORD(num));
		}
		return;

	case EHDR_CMD_T_E_SHSTRNDX:
		{
			Word num = ehdr->e_shstrndx;

			/*
			 * If using extended indexes, fetch the real
			 * value from shdr[0].sh_link
			 */
			if (num == SHN_XINDEX)
				num = argstate->obj_state->
				    os_secarr[0].sec_shdr->sh_link;

			elfedit_printf(MSG_ORIG(MSG_FMT_DECNUMNL),
			    EC_WORD(num));
		}
		return;

	case EHDR_CMD_T_EI_MAG0:
	case EHDR_CMD_T_EI_MAG1:
	case EHDR_CMD_T_EI_MAG2:
	case EHDR_CMD_T_EI_MAG3:
		/* This depends on EHDR_CMD_T_EI_MAG[0-3] being contiguous */
		c = ehdr->e_ident[cmd - EHDR_CMD_T_EI_MAG0];
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE)
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_magic_value(c));
		else
			elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL), c);
		return;

	case EHDR_CMD_T_EI_CLASS:
		c = ehdr->e_ident[EI_CLASS];
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE)
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_ehdr_class(c, 0, &inv_buf));
		else
			elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL), c);
		return;

	case EHDR_CMD_T_EI_DATA:
		c = ehdr->e_ident[EI_DATA];
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE)
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_ehdr_data(c, 0, &inv_buf));
		else
			elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL), c);
		return;

	case EHDR_CMD_T_EI_VERSION:
		c = ehdr->e_ident[EI_VERSION];
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE)
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_ehdr_vers(c, 0, &inv_buf));
		else
			elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL), c);
		return;

	case EHDR_CMD_T_EI_OSABI:
		c = ehdr->e_ident[EI_OSABI];
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_ehdr_osabi(c, 0, &inv_buf));
		} else {
			elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL),
			    EC_WORD(c));
		}
		return;

	case EHDR_CMD_T_EI_ABIVERSION:
		c = ehdr->e_ident[EI_ABIVERSION];
		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
			    conv_ehdr_abivers(ehdr->e_ident[EI_OSABI],
			    c, CONV_FMT_DECIMAL, &inv_buf));
		} else {
			elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL),
			    EC_WORD(c));
		}
		return;
	}
}


/*
 * Common body for the ehdr: 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 EHDR_CMD_T_* constants listed above, specifying
 *		which command to implement.
 *	obj_state, argc, argv - Standard command arguments
 */
static elfedit_cmdret_t
cmd_body(EHDR_CMD_T cmd, elfedit_obj_state_t *obj_state,
    int argc, const char *argv[])
{
	/*
	 * When a call comes in for ehdr:e_ident[ndx], and the
	 * specified element is one that we have a special command
	 * for, then we revector to that special command instead
	 * of using the generic ehdr:e_ident processing. This array,
	 * which is indexed by the e_ident[] index value is used
	 * to decide if that is the case. If the resulting value
	 * is EHDR_CMD_T_E_IDENT, then the generic processing is
	 * used. Otherwise, we revector to the specified command.
	 */
	static const int e_ident_revector[16] = {
		EHDR_CMD_T_EI_MAG0,		/* 0: EI_MAG0 */
		EHDR_CMD_T_EI_MAG1,		/* 1: EI_MAG1 */
		EHDR_CMD_T_EI_MAG2,		/* 2: EI_MAG2 */
		EHDR_CMD_T_EI_MAG3,		/* 3: EI_MAG3 */
		EHDR_CMD_T_EI_CLASS,		/* 4: EI_CLASS */
		EHDR_CMD_T_EI_DATA,		/* 5: EI_DATA */
		EHDR_CMD_T_EI_VERSION,		/* 6: EI_VERSION */
		EHDR_CMD_T_EI_OSABI,		/* 7: EI_OSABI */
		EHDR_CMD_T_EI_ABIVERSION,	/* 8: EI_ABIVERSION */
		EHDR_CMD_T_E_IDENT,		/* 9: generic */
		EHDR_CMD_T_E_IDENT,		/* 10: generic */
		EHDR_CMD_T_E_IDENT,		/* 11: generic */
		EHDR_CMD_T_E_IDENT,		/* 12: generic */
		EHDR_CMD_T_E_IDENT,		/* 13: generic */
		EHDR_CMD_T_E_IDENT,		/* 14: generic */
		EHDR_CMD_T_E_IDENT,		/* 15: generic */
	};


	ARGSTATE		argstate;
	Ehdr			*ehdr;
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	int			e_ident_ndx = -1;
	Conv_inv_buf_t		inv_buf1, inv_buf2;

	/* Process the optional arguments */
	process_args(obj_state, argc, argv, &argstate);

	/* Check number of arguments */
	switch (cmd) {
	case EHDR_CMD_T_DUMP:
		/* ehdr:dump does not accept arguments */
		if (argstate.argc > 0)
			elfedit_command_usage();
		break;
	case EHDR_CMD_T_E_IDENT:
		/*
		 * ehdr:e_ident accepts 1 or 2 arguments, the first
		 * being the index into the array, and the second being
		 * the value. If there are arguments, then process the
		 * index, and remove it from the argument list.
		 */
		if (argstate.argc > 0) {
			if (argstate.argc > 2)
				elfedit_command_usage();
			e_ident_ndx = (int)
			    elfedit_atoconst_range(argstate.argv[0],
			    MSG_ORIG(MSG_STR_INDEX), 0, EI_NIDENT - 1,
			    ELFEDIT_CONST_EI);
			argstate.argc--;
			argstate.argv++;

			/*
			 * If the index is for one of the e_ident elements
			 * that we have a special command for, then switch
			 * to that command. e_ident_revector[] returns
			 * EHDR_CMD_T_E_IDENT in the cases where such a command
			 * does not exist, in which case we'll continue with the
			 * generic code.
			 */
			cmd = e_ident_revector[e_ident_ndx];
		}
		break;
	case EHDR_CMD_T_E_FLAGS:
		/* ehdr:e_flags accepts an arbitrary number of arguments */
		break;
	default:
		/* The remaining commands accept a single optional argument */
		if (argstate.argc > 1)
			elfedit_command_usage();
		break;
	}

	/* If there are no arguments, dump the ELF header and return */
	if (argstate.argc == 0) {
		print_ehdr(cmd, e_ident_ndx, 0, &argstate);
		return (ELFEDIT_CMDRET_NONE);
	}

	ehdr = obj_state->os_ehdr;
	switch (cmd) {
		/*
		 * EHDR_CMD_T_DUMP can't get here: It never has an
		 * argument, and is handled above.
		 */

	case EHDR_CMD_T_E_IDENT:
		{
			/*
			 * Only those e_ident[] elements for which we
			 * don't have a specialized command come here.
			 * The argument is a value to be set in
			 * e_ident[e_ident_ndx].
			 */
			uchar_t value = (uchar_t)
			    elfedit_atoui_range(argstate.argv[0],
			    MSG_ORIG(MSG_STR_VALUE), 0, 255, NULL);

			if (ehdr->e_ident[e_ident_ndx] == value) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_D_X_OK),
				    e_ident_ndx, EC_WORD(value));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_D_X_CHG),
				    e_ident_ndx, ehdr->e_ident[e_ident_ndx],
				    value);
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_ident[e_ident_ndx] = value;
			}
		}
		break;

	case EHDR_CMD_T_E_TYPE:
		{
			/* The argument gives the object type */
			Half type = (Half) elfedit_atoconst(argstate.argv[0],
			    ELFEDIT_CONST_ET);
			const char *name = MSG_ORIG(MSG_CMD_E_TYPE);

			if (ehdr->e_type == type) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_S_OK), name,
				    conv_ehdr_type(ehdr->e_ident[EI_OSABI],
				    ehdr->e_type, 0, &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_S_CHG), name,
				    conv_ehdr_type(ehdr->e_ident[EI_OSABI],
				    ehdr->e_type, 0, &inv_buf1),
				    conv_ehdr_type(ehdr->e_ident[EI_OSABI],
				    type, 0, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_type = type;
			}
		}
		break;

	case EHDR_CMD_T_E_MACHINE:
		{
			/* The argument gives the machine code */
			Half mach = (Half) elfedit_atoconst(argstate.argv[0],
			    ELFEDIT_CONST_EM);
			const char *name = MSG_ORIG(MSG_CMD_E_MACHINE);

			if (ehdr->e_machine == mach) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_S_OK), name,
				    conv_ehdr_mach(ehdr->e_machine, 0,
				    &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_S_CHG), name,
				    conv_ehdr_mach(ehdr->e_machine, 0,
				    &inv_buf1),
				    conv_ehdr_mach(mach, 0, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD_OS_MACH;
				ehdr->e_machine = mach;

			}
		}
		break;

	case EHDR_CMD_T_E_VERSION:
		{
			/* The argument gives the version */
			Word ver = (Word) elfedit_atoconst(argstate.argv[0],
			    ELFEDIT_CONST_EV);
			const char *name = MSG_ORIG(MSG_CMD_E_VERSION);

			if (ehdr->e_version == ver) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_S_OK), name,
				    conv_ehdr_vers(ehdr->e_version, 0,
				    &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_S_CHG), name,
				    conv_ehdr_vers(ehdr->e_version, 0,
				    &inv_buf1),
				    conv_ehdr_vers(ver, 0, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_version = ver;
			}
		}
		break;

	case EHDR_CMD_T_E_ENTRY:
		{
			/* The argument gives the entry address */
			Addr entry = (Addr)
			    elfedit_atoui(argstate.argv[0], NULL);
			const char *name = MSG_ORIG(MSG_CMD_E_ENTRY);

			if (ehdr->e_entry == entry) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_LLX_OK), name,
				    EC_ADDR(ehdr->e_entry));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_LLX_CHG), name,
				    EC_ADDR(ehdr->e_entry), EC_ADDR(entry));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_entry = entry;
			}
		}
		break;

	case EHDR_CMD_T_E_PHOFF:
		{
			/* The argument gives the program header offset */
			Off off = (Off) elfedit_atoui(argstate.argv[0],
			    NULL);
			const char *name = MSG_ORIG(MSG_CMD_E_PHOFF);

			if (ehdr->e_phoff == off) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_LLX_OK), name,
				    EC_OFF(ehdr->e_phoff));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_LLX_CHG), name,
				    EC_OFF(ehdr->e_phoff), EC_OFF(off));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_phoff = off;
			}
		}
		break;

	case EHDR_CMD_T_E_SHOFF:
		{
			/* The argument gives the section header offset */
			Off off = (Off) elfedit_atoui(argstate.argv[0],
			    NULL);
			const char *name = MSG_ORIG(MSG_CMD_E_SHOFF);

			if (ehdr->e_shoff == off) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_LLX_OK), name,
				    EC_OFF(ehdr->e_shoff));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_LLX_CHG), name,
				    EC_OFF(ehdr->e_shoff), EC_OFF(off));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_shoff = off;
			}
		}
		break;

	case EHDR_CMD_T_E_FLAGS:
		{
			Conv_ehdr_flags_buf_t flags_buf1, flags_buf2;
			const char *name = MSG_ORIG(MSG_CMD_E_FLAGS);
			Word flags = 0;
			int i;

			/* Collect the arguments */
			for (i = 0; i < argstate.argc; i++)
				flags |= (Word)
				    elfedit_atoconst(argstate.argv[i],
				    ELFEDIT_CONST_EF);

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

			/* Perform any requested bit operations */
			if (argstate.optmask & EHDR_OPT_F_AND)
				flags &= ehdr->e_flags;
			else if (argstate.optmask & EHDR_OPT_F_OR)
				flags |= ehdr->e_flags;

			/* Set the value */
			if (ehdr->e_flags == flags) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_S_OK), name,
				    conv_ehdr_flags(ehdr->e_machine,
				    ehdr->e_flags, 0, &flags_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_S_CHG), name,
				    conv_ehdr_flags(ehdr->e_machine,
				    ehdr->e_flags, 0, &flags_buf1),
				    conv_ehdr_flags(ehdr->e_machine,
				    flags, 0, &flags_buf2));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_flags = flags;
			}
		}
		break;

	case EHDR_CMD_T_E_EHSIZE:
		{
			/* The argument gives the ELF header size */
			Half ehsize = (Half) elfedit_atoui(argstate.argv[0],
			    NULL);
			const char *name = MSG_ORIG(MSG_CMD_E_EHSIZE);

			if (ehdr->e_ehsize == ehsize) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_OK), name,
				    EC_WORD(ehdr->e_ehsize));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_CHG), name,
				    EC_WORD(ehdr->e_ehsize), EC_WORD(ehsize));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_ehsize = ehsize;
			}
		}
		break;

	case EHDR_CMD_T_E_PHENTSIZE:
		{
			/*
			 * The argument gives the size of a program
			 * header element.
			 */
			Half phentsize = (Half) elfedit_atoui(argstate.argv[0],
			    NULL);
			const char *name = MSG_ORIG(MSG_CMD_E_PHENTSIZE);

			if (ehdr->e_phentsize == phentsize) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_OK), name,
				    EC_WORD(ehdr->e_phentsize));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_CHG), name,
				    EC_WORD(ehdr->e_phentsize),
				    EC_WORD(phentsize));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_phentsize = phentsize;
			}
		}
		break;

	case EHDR_CMD_T_E_PHNUM:
		{
			/* The argument gives the number of program headers */
			Word phnum = (Word) elfedit_atoui(argstate.argv[0],
			    NULL);
			const char *name = MSG_ORIG(MSG_CMD_E_PHNUM);
			elfedit_section_t *sec0 = &obj_state->os_secarr[0];
			Shdr *shdr0 = sec0->sec_shdr;
			Half e_phnum;
			Word sh_info;

			if (phnum >= PN_XNUM) {
				e_phnum = PN_XNUM;
				sh_info = phnum;
			} else {
				e_phnum = phnum;
				sh_info = 0;
			}

			if (ehdr->e_phnum == e_phnum) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_OK), name,
				    EC_WORD(ehdr->e_phnum));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_CHG), name,
				    EC_WORD(ehdr->e_phnum), e_phnum);
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_phnum = e_phnum;
			}
			if (shdr0->sh_info == sh_info) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_SHDR0_D_OK),
				    MSG_ORIG(MSG_STR_SH_INFO),
				    EC_WORD(shdr0->sh_info));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_SHDR0_D_CHG),
				    MSG_ORIG(MSG_STR_SH_INFO),
				    EC_WORD(shdr0->sh_info), sh_info);
				ret = ELFEDIT_CMDRET_MOD;
				shdr0->sh_info = sh_info;
				elfedit_modified_shdr(sec0);
			}
		}
		break;

	case EHDR_CMD_T_E_SHENTSIZE:
		{
			/*
			 * The argument gives the size of a program
			 * header element.
			 */
			Half shentsize = (Half) elfedit_atoui(argstate.argv[0],
			    NULL);
			const char *name = MSG_ORIG(MSG_CMD_E_SHENTSIZE);

			if (ehdr->e_shentsize == shentsize) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_OK), name,
				    EC_WORD(ehdr->e_shentsize));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_CHG), name,
				    EC_WORD(ehdr->e_shentsize),
				    EC_WORD(shentsize));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_shentsize = shentsize;
			}
		}
		break;

	case EHDR_CMD_T_E_SHNUM:
		{
			/* The argument gives the number of section headers */
			Word shnum = (Word) elfedit_atoui(argstate.argv[0],
			    NULL);
			const char *name = MSG_ORIG(MSG_CMD_E_SHNUM);
			elfedit_section_t *sec0 = &obj_state->os_secarr[0];
			Shdr *shdr0 = sec0->sec_shdr;
			Half e_shnum;
			Word sh_size;

			if (shnum >= SHN_LORESERVE) {
				e_shnum = 0;
				sh_size = shnum;
			} else {
				e_shnum = shnum;
				sh_size = 0;
			}

			if (ehdr->e_shnum == e_shnum) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_OK), name,
				    EC_WORD(ehdr->e_shnum));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_CHG), name,
				    EC_WORD(ehdr->e_shnum), e_shnum);
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_shnum = e_shnum;
			}
			if (shdr0->sh_size == sh_size) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_SHDR0_D_OK),
				    MSG_ORIG(MSG_STR_SH_SIZE),
				    EC_WORD(shdr0->sh_size));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_SHDR0_D_CHG),
				    MSG_ORIG(MSG_STR_SH_SIZE),
				    EC_WORD(shdr0->sh_size), sh_size);
				ret = ELFEDIT_CMDRET_MOD;
				shdr0->sh_size = sh_size;
				elfedit_modified_shdr(sec0);
			}
		}
		break;

	case EHDR_CMD_T_E_SHSTRNDX:
		{
			const char *name = MSG_ORIG(MSG_CMD_E_SHSTRNDX);
			Word shstrndx;
			elfedit_section_t *sec0 = &obj_state->os_secarr[0];
			Shdr *shdr0 = sec0->sec_shdr;
			Half e_shstrndx;
			Word sh_link;

			/*
			 * By default, sec argument is name of section.
			 * If -shndx is used, it is a numeric index, and
			 * if -shtyp is used, it is a section type.
			 */
			if (argstate.optmask & EHDR_OPT_F_SHNDX)
				shstrndx = elfedit_atoshndx(argstate.argv[0],
				    obj_state->os_shnum);
			else if (argstate.optmask & EHDR_OPT_F_SHTYP)
				shstrndx = elfedit_type_to_shndx(obj_state,
				    elfedit_atoconst(argstate.argv[0],
				    ELFEDIT_CONST_SHT));
			else
				shstrndx = elfedit_name_to_shndx(obj_state,
				    argstate.argv[0]);

			/* Warn if the section isn't a string table */
			if ((shstrndx >= obj_state->os_shnum) ||
			    ((shstrndx >= SHN_LORESERVE) &&
			    (shstrndx <= SHN_HIRESERVE)) ||
			    (obj_state->os_secarr[shstrndx].sec_shdr->sh_type !=
			    SHT_STRTAB))
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_NOTSTRTAB), name,
				    EC_WORD(shstrndx));

			if (shstrndx >= SHN_LORESERVE) {
				e_shstrndx = SHN_XINDEX;
				sh_link = shstrndx;
			} else {
				e_shstrndx = shstrndx;
				sh_link = 0;
			}

			if (ehdr->e_shstrndx == e_shstrndx) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_OK), name,
				    EC_WORD(ehdr->e_shstrndx));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_E_D_CHG), name,
				    EC_WORD(ehdr->e_shstrndx), e_shstrndx);
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_shstrndx = e_shstrndx;
			}
			if (shdr0->sh_link == sh_link) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_SHDR0_D_OK),
				    MSG_ORIG(MSG_STR_SH_LINK),
				    EC_WORD(shdr0->sh_link));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_SHDR0_D_CHG),
				    MSG_ORIG(MSG_STR_SH_LINK),
				    EC_WORD(shdr0->sh_link), sh_link);
				ret = ELFEDIT_CMDRET_MOD;
				shdr0->sh_link = sh_link;
				elfedit_modified_shdr(sec0);
			}
		}
		break;

	case EHDR_CMD_T_EI_MAG0:
	case EHDR_CMD_T_EI_MAG1:
	case EHDR_CMD_T_EI_MAG2:
	case EHDR_CMD_T_EI_MAG3:
		{
			/*
			 * This depends on EHDR_CMD_T_EI_MAG[0-3]
			 * being contiguous
			 */
			int ei_ndx = (cmd - EHDR_CMD_T_EI_MAG0) + EI_MAG0;

			/* The argument gives the magic number byte */
			int mag = (int)elfedit_atoui_range(argstate.argv[0],
			    MSG_ORIG(MSG_STR_VALUE), 0, 255, NULL);

			if (ehdr->e_ident[ei_ndx] == mag) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_OK),
				    elfedit_atoconst_value_to_str(
				    ELFEDIT_CONST_EI, ei_ndx, 1),
				    conv_magic_value(ehdr->e_ident[ei_ndx]));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_CHG),
				    elfedit_atoconst_value_to_str(
				    ELFEDIT_CONST_EI, ei_ndx, 1),
				    conv_magic_value(ehdr->e_ident[ei_ndx]),
				    conv_magic_value(mag));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_ident[ei_ndx] = mag;
			}
		}
		break;

	case EHDR_CMD_T_EI_CLASS:
		{
			/* The argument gives the ELFCLASS value */
			int class = (int)elfedit_atoconst_range(
			    argstate.argv[0], MSG_ORIG(MSG_STR_VALUE), 0, 255,
			    ELFEDIT_CONST_ELFCLASS);
			const char *name = elfedit_atoconst_value_to_str(
			    ELFEDIT_CONST_EI, EI_CLASS, 1);

			if (ehdr->e_ident[EI_CLASS] == class) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_OK), name,
				    conv_ehdr_class(class, 0, &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_CHG), name,
				    conv_ehdr_class(ehdr->e_ident[EI_CLASS],
				    0, &inv_buf1),
				    conv_ehdr_class(class, 0, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_ident[EI_CLASS] = class;
			}
		}
		break;

	case EHDR_CMD_T_EI_DATA:
		{
			/* The argument gives the ELFDATA value */
			int data = (int)elfedit_atoconst_range(argstate.argv[0],
			    MSG_ORIG(MSG_STR_VALUE), 0, 255,
			    ELFEDIT_CONST_ELFDATA);
			const char *name = elfedit_atoconst_value_to_str(
			    ELFEDIT_CONST_EI, EI_DATA, 1);

			if (ehdr->e_ident[EI_DATA] == data) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_OK), name,
				    conv_ehdr_data(data, 0, &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_CHG), name,
				    conv_ehdr_data(ehdr->e_ident[EI_DATA],
				    0, &inv_buf1),
				    conv_ehdr_data(data, 0, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_ident[EI_DATA] = data;
			}
		}
		break;

	case EHDR_CMD_T_EI_VERSION:
		{
			/* The argument gives the version */
			int ver = (int)elfedit_atoconst_range(argstate.argv[0],
			    MSG_ORIG(MSG_STR_VALUE), 0, 255, ELFEDIT_CONST_EV);
			const char *name = elfedit_atoconst_value_to_str(
			    ELFEDIT_CONST_EI, EI_VERSION, 1);

			if (ehdr->e_ident[EI_VERSION] == ver) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_OK), name,
				    conv_ehdr_vers(ver, 0, &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_CHG), name,
				    conv_ehdr_vers(ehdr->e_ident[EI_VERSION],
				    0, &inv_buf1),
				    conv_ehdr_vers(ver, 0, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_ident[EI_VERSION] = ver;
			}
		}
		break;

	case EHDR_CMD_T_EI_OSABI:
		{
			/* The argument gives the ABI code */
			int osabi = (int)elfedit_atoconst_range(
			    argstate.argv[0], MSG_ORIG(MSG_STR_VALUE), 0, 255,
			    ELFEDIT_CONST_ELFOSABI);
			const char *name = elfedit_atoconst_value_to_str(
			    ELFEDIT_CONST_EI, EI_OSABI, 1);

			if (ehdr->e_ident[EI_OSABI] == osabi) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_OK), name,
				    conv_ehdr_osabi(osabi, 0, &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_CHG), name,
				    conv_ehdr_osabi(ehdr->e_ident[EI_OSABI],
				    0, &inv_buf1),
				    conv_ehdr_osabi(osabi, 0, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD_OS_MACH;
				ehdr->e_ident[EI_OSABI] = osabi;
			}
		}
		break;

	case EHDR_CMD_T_EI_ABIVERSION:
		{
			/* The argument gives the ABI version  */
			int abiver = (int)elfedit_atoconst_range(
			    argstate.argv[0], MSG_ORIG(MSG_STR_VALUE), 0, 255,
			    ELFEDIT_CONST_EAV);
			const char *name = elfedit_atoconst_value_to_str(
			    ELFEDIT_CONST_EI, EI_ABIVERSION, 1);

			if (ehdr->e_ident[EI_ABIVERSION] == abiver) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_OK), name,
				    conv_ehdr_abivers(ehdr->e_ident[EI_OSABI],
				    abiver, CONV_FMT_DECIMAL, &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_EI_S_S_CHG), name,
				    conv_ehdr_abivers(ehdr->e_ident[EI_OSABI],
				    ehdr->e_ident[EI_ABIVERSION],
				    CONV_FMT_DECIMAL, &inv_buf1),
				    conv_ehdr_abivers(ehdr->e_ident[EI_OSABI],
				    abiver, CONV_FMT_DECIMAL, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD;
				ehdr->e_ident[EI_ABIVERSION] = abiver;
			}
		}
		break;
	}

	/*
	 * If we modified the ELF header, tell libelf.
	 */
	if (ret == ELFEDIT_CMDRET_MOD)
		elfedit_modified_ehdr(obj_state);

	/* Do autoprint */
	print_ehdr(cmd, e_ident_ndx, 1, &argstate);

	return (ret);
}




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

/*ARGSUSED*/
static void
cpl_e_ident(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	elfedit_atoui_t	ndx;

	/*
	 * This command doesn't accept options, so num_opt should be
	 * 0. This is a defensive measure, in case that should change.
	 */
	argc -= num_opt;
	argv += num_opt;

	if (argc == 1) {
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_EI);
		return;
	}

	if (argc != 2)
		return;

	/*
	 * In order to offer up the right completion strings for
	 * the value, we need to know what index was given for
	 * the first argument. If we don't recognize the index,
	 * we want to return quietly without issuing an error,
	 * so we use elfedit_atoui_range2(), which returns
	 * a success/failure result and does not throw any errors.
	 */
	if (elfedit_atoconst_range2(argv[0], 0, EI_NIDENT - 1,
	    ELFEDIT_CONST_EI, &ndx) == 0)
		return;
	switch (ndx) {
	case EI_CLASS:
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_ELFCLASS);
		break;
	case EI_DATA:
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_ELFDATA);
		break;
	case EI_VERSION:
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_EV);
		break;
	case EI_OSABI:
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_ELFOSABI);
		break;
	}
}

/*ARGSUSED*/
static void
cpl_e_type(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/*
	 * This command doesn't accept options, so num_opt should be
	 * 0. This is a defensive measure, in case that should change.
	 */
	argc -= num_opt;
	argv += num_opt;

	if (argc == 1)
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_ET);
}

/*ARGSUSED*/
static void
cpl_e_machine(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/*
	 * This command doesn't accept options, so num_opt should be
	 * 0. This is a defensive measure, in case that should change.
	 */
	argc -= num_opt;
	argv += num_opt;

	if (argc == 1)
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_EM);
}

/*ARGSUSED*/
static void
cpl_e_version(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/*
	 * This command doesn't accept options, so num_opt should be
	 * 0. This is a defensive measure, in case that should change.
	 */
	argc -= num_opt;
	argv += num_opt;

	if (argc == 1)
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_EV);
}

/*ARGSUSED*/
static void
cpl_e_flags(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/* This routine allows multiple flags to be specified */
	elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_EF);
}

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

	/*
	 * The plainargument can be a section name, index, or
	 * type, based on the options used. All have completions.
	 */
	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;
	}

	if (obj_state == NULL) {	/* No object available */
		if (op == TYPE)
			elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SHT);
		return;
	}

	/*
	 * Loop over the sections and supply command completion
	 * for the string tables in the file.
	 */
	for (ndx = 0; ndx < obj_state->os_shnum; ndx++) {
		elfedit_section_t *sec = &obj_state->os_secarr[ndx];

		if (sec->sec_shdr->sh_type != SHT_STRTAB)
			continue;

		switch (op) {
		case NAME:
			elfedit_cpl_match(cpldata, sec->sec_name, 0);
			break;
		case INDEX:
			elfedit_cpl_ndx(cpldata, ndx);
			break;
		case TYPE:
			elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SHT_STRTAB);
			break;
		}
	}
}

/*ARGSUSED*/
static void
cpl_ei_class(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/*
	 * This command doesn't accept options, so num_opt should be
	 * 0. This is a defensive measure, in case that should change.
	 */
	argc -= num_opt;
	argv += num_opt;

	if (argc == 1)
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_ELFCLASS);
}

/*ARGSUSED*/
static void
cpl_ei_data(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/*
	 * This command doesn't accept options, so num_opt should be
	 * 0. This is a defensive measure, in case that should change.
	 */
	argc -= num_opt;
	argv += num_opt;

	if (argc == 1)
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_ELFDATA);
}

/*ARGSUSED*/
static void
cpl_ei_osabi(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/*
	 * This command doesn't accept options, so num_opt should be
	 * 0. This is a defensive measure, in case that should change.
	 */
	argc -= num_opt;
	argv += num_opt;

	if (argc == 1)
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_ELFOSABI);
}

/*ARGSUSED*/
static void
cpl_ei_abiversion(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/*
	 * This command doesn't accept options, so num_opt should be
	 * 0. This is a defensive measure, in case that should change.
	 */
	argc -= num_opt;
	argv += num_opt;

	if (argc == 1)
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_EAV);
}




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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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




/*ARGSUSED*/
elfedit_module_t *
elfedit_init(elfedit_module_version_t version)
{
	/* Many of the commands only accept -o */
	static elfedit_cmd_optarg_t opt_std[] = {
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ NULL }
	};


	/* ehdr:dump */
	static const char *name_dump[] = {
	    MSG_ORIG(MSG_CMD_DUMP),
	    MSG_ORIG(MSG_STR_EMPTY),	/* "" makes this the default command */
	    NULL
	};

	/* ehdr:e_ident */
	static const char *name_e_ident[] = {
		MSG_ORIG(MSG_CMD_E_IDENT), NULL };
	static elfedit_cmd_optarg_t arg_e_ident[] = {
		{ MSG_ORIG(MSG_STR_INDEX),
		    /* MSG_INTL(MSG_ARGDESC_E_IDENT_NDX) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_IDENT_NDX),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_E_IDENT_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_IDENT_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_type */
	static const char *name_e_type[] = {
		MSG_ORIG(MSG_CMD_E_TYPE), NULL };
	static elfedit_cmd_optarg_t arg_e_type[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_E_TYPE_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_TYPE_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_machine */
	static const char *name_e_machine[] = {
		MSG_ORIG(MSG_CMD_E_MACHINE), NULL };
	static elfedit_cmd_optarg_t arg_e_machine[] = {
		{ MSG_ORIG(MSG_STR_TYPE),
		    /* MSG_INTL(MSG_ARGDESC_E_MACHINE_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_MACHINE_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_version */
	static const char *name_e_version[] = {
		MSG_ORIG(MSG_CMD_E_VERSION), NULL };
	static elfedit_cmd_optarg_t arg_e_version[] = {
		{ MSG_ORIG(MSG_STR_VERSION),
		    /* MSG_INTL(MSG_ARGDESC_E_VERSION_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_VERSION_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_entry */
	static const char *name_e_entry[] = {
		MSG_ORIG(MSG_CMD_E_ENTRY), NULL };
	static elfedit_cmd_optarg_t arg_e_entry[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_E_ENTRY_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_ENTRY_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_phoff */
	static const char *name_e_phoff[] = {
		MSG_ORIG(MSG_CMD_E_PHOFF), NULL };
	static elfedit_cmd_optarg_t arg_e_phoff[] = {
		{ MSG_ORIG(MSG_STR_OFFSET),
		    /* MSG_INTL(MSG_ARGDESC_E_PHOFF_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_PHOFF_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_shoff */
	static const char *name_e_shoff[] = {
		MSG_ORIG(MSG_CMD_E_SHOFF), NULL };
	static elfedit_cmd_optarg_t arg_e_shoff[] = {
		{ MSG_ORIG(MSG_STR_OFFSET),
		    /* MSG_INTL(MSG_ARGDESC_E_SHOFF_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_SHOFF_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_flags */
	static const char *name_e_flags[] = {
		MSG_ORIG(MSG_CMD_E_FLAGS), NULL };
	static elfedit_cmd_optarg_t opt_e_flags[] = {
		{ ELFEDIT_STDOA_OPT_AND, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, EHDR_OPT_F_AND, EHDR_OPT_F_OR },
		{ ELFEDIT_STDOA_OPT_CMP, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, EHDR_OPT_F_CMP, 0 },
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ ELFEDIT_STDOA_OPT_OR, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, EHDR_OPT_F_OR, EHDR_OPT_F_AND },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_e_flags[] = {
		{ MSG_ORIG(MSG_STR_FLAGVALUE),
		    /* MSG_INTL(MSG_ARGDESC_E_FLAGS_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_FLAGS_VALUE),
		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT, 0 },
		{ NULL }
	};

	/* ehdr:e_ehsize */
	static const char *name_e_ehsize[] = {
		MSG_ORIG(MSG_CMD_E_EHSIZE), NULL };
	static elfedit_cmd_optarg_t arg_e_ehsize[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_E_EHSIZE_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_EHSIZE_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_phentsize */
	static const char *name_e_phentsize[] = {
		MSG_ORIG(MSG_CMD_E_PHENTSIZE), NULL };
	static elfedit_cmd_optarg_t arg_e_phentsize[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_E_PHENTSIZE_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_PHENTSIZE_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_phnum */
	static const char *name_e_phnum[] = {
		MSG_ORIG(MSG_CMD_E_PHNUM), NULL };
	static elfedit_cmd_optarg_t arg_e_phnum[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_E_PHNUM_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_PHNUM_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_shentsize */
	static const char *name_e_shentsize[] = {
		MSG_ORIG(MSG_CMD_E_SHENTSIZE), NULL };
	static elfedit_cmd_optarg_t arg_e_shentsize[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_E_SHENTSIZE_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_SHENTSIZE_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_shnum */
	static const char *name_e_shnum[] = {
		MSG_ORIG(MSG_CMD_E_SHNUM), NULL };
	static elfedit_cmd_optarg_t arg_e_shnum[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_E_SHNUM_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_SHNUM_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:e_shstrndx */
	static const char *name_e_shstrndx[] = {
		MSG_ORIG(MSG_CMD_E_SHSTRNDX), NULL };
	static elfedit_cmd_optarg_t opt_e_shstrndx[] = {
		{ 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,
		    EHDR_OPT_F_SHNDX, EHDR_OPT_F_SHTYP },
		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), 0,
		    EHDR_OPT_F_SHTYP, EHDR_OPT_F_SHNDX,  },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_e_shstrndx[] = {
		{ MSG_ORIG(MSG_STR_SEC),
		    /* MSG_INTL(MSG_ARGDESC_E_SHSTRNDX_SEC) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_E_SHSTRNDX_SEC),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:ei_mag0 */
	static const char *name_ei_mag0[] = {
		MSG_ORIG(MSG_CMD_EI_MAG0), NULL };
	static elfedit_cmd_optarg_t arg_ei_mag0[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_EI_MAG0_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_EI_MAG0_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:ei_mag1 */
	static const char *name_ei_mag1[] = {
		MSG_ORIG(MSG_CMD_EI_MAG1), NULL };
	static elfedit_cmd_optarg_t arg_ei_mag1[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_EI_MAG1_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_EI_MAG1_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:ei_mag2 */
	static const char *name_ei_mag2[] = {
		MSG_ORIG(MSG_CMD_EI_MAG2), NULL };
	static elfedit_cmd_optarg_t arg_ei_mag2[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_EI_MAG2_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_EI_MAG2_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:ei_mag3 */
	static const char *name_ei_mag3[] = {
		MSG_ORIG(MSG_CMD_EI_MAG3), NULL };
	static elfedit_cmd_optarg_t arg_ei_mag3[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_EI_MAG3_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_EI_MAG3_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:ei_class */
	static const char *name_ei_class[] = {
		MSG_ORIG(MSG_CMD_EI_CLASS), NULL };
	static elfedit_cmd_optarg_t arg_ei_class[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_EI_CLASS_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_EI_CLASS_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:ei_data */
	static const char *name_ei_data[] = {
		MSG_ORIG(MSG_CMD_EI_DATA), NULL };
	static elfedit_cmd_optarg_t arg_ei_data[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_EI_DATA_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_EI_DATA_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:ei_version */
	static const char *name_ei_version[] = {
		MSG_ORIG(MSG_CMD_EI_VERSION), NULL };
	/* Note: arg_e_version is also used for this command */

	/* ehdr:ei_osabi */
	static const char *name_ei_osabi[] = {
		MSG_ORIG(MSG_CMD_EI_OSABI), NULL };
	static elfedit_cmd_optarg_t arg_ei_osabi[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_EI_OSABI_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_EI_OSABI_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};

	/* ehdr:ei_abiversion */
	static const char *name_ei_abiversion[] = {
		MSG_ORIG(MSG_CMD_EI_ABIVERSION), NULL };
	static elfedit_cmd_optarg_t arg_ei_abiversion[] = {
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_ARGDESC_EI_ABIVERSION_VALUE) */
		    ELFEDIT_I18NHDL(MSG_ARGDESC_EI_ABIVERSION_VALUE),
		    ELFEDIT_CMDOA_F_OPT, 0 },
		{ NULL }
	};




	static elfedit_cmd_t cmds[] = {
		/* ehdr:dump */
		{ cmd_dump, NULL, name_dump,
		    /* MSG_INTL(MSG_DESC_DUMP) */
		    ELFEDIT_I18NHDL(MSG_DESC_DUMP),
		    /* MSG_INTL(MSG_HELP_DUMP) */
		    ELFEDIT_I18NHDL(MSG_HELP_DUMP),
		    NULL, NULL },

		/* ehdr:e_ident */
		{ cmd_e_ident, cpl_e_ident, name_e_ident,
		    /* MSG_INTL(MSG_DESC_E_IDENT) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_IDENT),
		    /* MSG_INTL(MSG_HELP_E_IDENT) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_IDENT),
		    opt_std, arg_e_ident },

		/* ehdr:e_type */
		{ cmd_e_type, cpl_e_type, name_e_type,
		    /* MSG_INTL(MSG_DESC_E_TYPE) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_TYPE),
		    /* MSG_INTL(MSG_HELP_E_TYPE) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_TYPE),
		    opt_std, arg_e_type },

		/* ehdr:e_machine */
		{ cmd_e_machine, cpl_e_machine, name_e_machine,
		    /* MSG_INTL(MSG_DESC_E_MACHINE) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_MACHINE),
		    /* MSG_INTL(MSG_HELP_E_MACHINE) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_MACHINE),
		    opt_std, arg_e_machine },

		/* ehdr:e_version */
		{ cmd_e_version, cpl_e_version, name_e_version,
		    /* MSG_INTL(MSG_DESC_E_VERSION) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_VERSION),
		    /* MSG_INTL(MSG_HELP_E_VERSION) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_VERSION),
		    opt_std, arg_e_version },

		/* ehdr:e_entry */
		{ cmd_e_entry, NULL, name_e_entry,
		    /* MSG_INTL(MSG_DESC_E_ENTRY) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_ENTRY),
		    /* MSG_INTL(MSG_HELP_E_ENTRY) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_ENTRY),
		    opt_std, arg_e_entry },

		/* ehdr:e_phoff */
		{ cmd_e_phoff, NULL, name_e_phoff,
		    /* MSG_INTL(MSG_DESC_E_PHOFF) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_PHOFF),
		    /* MSG_INTL(MSG_HELP_E_PHOFF) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_PHOFF),
		    opt_std, arg_e_phoff },

		/* ehdr:e_shoff */
		{ cmd_e_shoff, NULL, name_e_shoff,
		    /* MSG_INTL(MSG_DESC_E_SHOFF) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_SHOFF),
		    /* MSG_INTL(MSG_HELP_E_SHOFF) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_SHOFF),
		    opt_std, arg_e_shoff },

		/* ehdr:e_flags */
		{ cmd_e_flags, cpl_e_flags, name_e_flags,
		    /* MSG_INTL(MSG_DESC_E_FLAGS) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_FLAGS),
		    /* MSG_INTL(MSG_HELP_E_FLAGS) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_FLAGS),
		    opt_e_flags, arg_e_flags },

		/* ehdr:e_ehsize */
		{ cmd_e_ehsize, NULL, name_e_ehsize,
		    /* MSG_INTL(MSG_DESC_E_EHSIZE) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_EHSIZE),
		    /* MSG_INTL(MSG_HELP_E_EHSIZE) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_EHSIZE),
		    opt_std, arg_e_ehsize },

		/* ehdr:e_phentsize */
		{ cmd_e_phentsize, NULL, name_e_phentsize,
		    /* MSG_INTL(MSG_DESC_E_PHENTSIZE) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_PHENTSIZE),
		    /* MSG_INTL(MSG_HELP_E_PHENTSIZE) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_PHENTSIZE),
		    opt_std, arg_e_phentsize },

		/* ehdr:e_phnum */
		{ cmd_e_phnum, NULL, name_e_phnum,
		    /* MSG_INTL(MSG_DESC_E_PHNUM) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_PHNUM),
		    /* MSG_INTL(MSG_HELP_E_PHNUM) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_PHNUM),
		    opt_std, arg_e_phnum },

		/* ehdr:e_shentsize */
		{ cmd_e_shentsize, NULL, name_e_shentsize,
		    /* MSG_INTL(MSG_DESC_E_SHENTSIZE) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_SHENTSIZE),
		    /* MSG_INTL(MSG_HELP_E_SHENTSIZE) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_SHENTSIZE),
		    opt_std, arg_e_shentsize },

		/* ehdr:e_shnum */
		{ cmd_e_shnum, NULL, name_e_shnum,
		    /* MSG_INTL(MSG_DESC_E_SHNUM) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_SHNUM),
		    /* MSG_INTL(MSG_HELP_E_SHNUM) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_SHNUM),
		    opt_std, arg_e_shnum },

		/* ehdr:e_shstrndx */
		{ cmd_e_shstrndx, cpl_e_shstrndx, name_e_shstrndx,
		    /* MSG_INTL(MSG_DESC_E_SHSTRNDX) */
		    ELFEDIT_I18NHDL(MSG_DESC_E_SHSTRNDX),
		    /* MSG_INTL(MSG_HELP_E_SHSTRNDX) */
		    ELFEDIT_I18NHDL(MSG_HELP_E_SHSTRNDX),
		    opt_e_shstrndx, arg_e_shstrndx },

		/* ehdr:ei_mag0 */
		{ cmd_ei_mag0, NULL, name_ei_mag0,
		    /* MSG_INTL(MSG_DESC_EI_MAG0) */
		    ELFEDIT_I18NHDL(MSG_DESC_EI_MAG0),
		    /* MSG_INTL(MSG_HELP_EI_MAG0) */
		    ELFEDIT_I18NHDL(MSG_HELP_EI_MAG0),
		    opt_std, arg_ei_mag0 },

		/* ehdr:ei_mag1 */
		{ cmd_ei_mag1, NULL, name_ei_mag1,
		    /* MSG_INTL(MSG_DESC_EI_MAG1) */
		    ELFEDIT_I18NHDL(MSG_DESC_EI_MAG1),
		    /* MSG_INTL(MSG_HELP_EI_MAG1) */
		    ELFEDIT_I18NHDL(MSG_HELP_EI_MAG1),
		    opt_std, arg_ei_mag1 },

		/* ehdr:ei_mag2 */
		{ cmd_ei_mag2, NULL, name_ei_mag2,
		    /* MSG_INTL(MSG_DESC_EI_MAG2) */
		    ELFEDIT_I18NHDL(MSG_DESC_EI_MAG2),
		    /* MSG_INTL(MSG_HELP_EI_MAG2) */
		    ELFEDIT_I18NHDL(MSG_HELP_EI_MAG2),
		    opt_std, arg_ei_mag2 },

		/* ehdr:ei_mag3 */
		{ cmd_ei_mag3, NULL, name_ei_mag3,
		    /* MSG_INTL(MSG_DESC_EI_MAG3) */
		    ELFEDIT_I18NHDL(MSG_DESC_EI_MAG3),
		    /* MSG_INTL(MSG_HELP_EI_MAG3) */
		    ELFEDIT_I18NHDL(MSG_HELP_EI_MAG3),
		    opt_std, arg_ei_mag3 },

		/* ehdr:ei_class */
		{ cmd_ei_class, cpl_ei_class, name_ei_class,
		    /* MSG_INTL(MSG_DESC_EI_CLASS) */
		    ELFEDIT_I18NHDL(MSG_DESC_EI_CLASS),
		    /* MSG_INTL(MSG_HELP_EI_CLASS) */
		    ELFEDIT_I18NHDL(MSG_HELP_EI_CLASS),
		    opt_std, arg_ei_class },

		/* ehdr:ei_data */
		{ cmd_ei_data, cpl_ei_data, name_ei_data,
		    /* MSG_INTL(MSG_DESC_EI_DATA) */
		    ELFEDIT_I18NHDL(MSG_DESC_EI_DATA),
		    /* MSG_INTL(MSG_HELP_EI_DATA) */
		    ELFEDIT_I18NHDL(MSG_HELP_EI_DATA),
		    opt_std, arg_ei_data },

		/* ehdr:ei_version */
		{ cmd_ei_version, cpl_e_version, name_ei_version,
		    /* MSG_INTL(MSG_DESC_EI_VERSION) */
		    ELFEDIT_I18NHDL(MSG_DESC_EI_VERSION),
		    /* MSG_INTL(MSG_HELP_EI_VERSION) */
		    ELFEDIT_I18NHDL(MSG_HELP_EI_VERSION),
		    opt_std, arg_e_version },

		/* ehdr:ei_osabi */
		{ cmd_ei_osabi, cpl_ei_osabi, name_ei_osabi,
		    /* MSG_INTL(MSG_DESC_EI_OSABI) */
		    ELFEDIT_I18NHDL(MSG_DESC_EI_OSABI),
		    /* MSG_INTL(MSG_HELP_EI_OSABI) */
		    ELFEDIT_I18NHDL(MSG_HELP_EI_OSABI),
		    opt_std, arg_ei_osabi },

		/* ehdr:ei_abiversion */
		{ cmd_ei_abiversion, cpl_ei_abiversion, name_ei_abiversion,
		    /* MSG_INTL(MSG_DESC_EI_ABIVERSION) */
		    ELFEDIT_I18NHDL(MSG_DESC_EI_ABIVERSION),
		    /* MSG_INTL(MSG_HELP_EI_ABIVERSION) */
		    ELFEDIT_I18NHDL(MSG_HELP_EI_ABIVERSION),
		    opt_std, arg_ei_abiversion },

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