/*
 * 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	<elfedit.h>
#include	<strings.h>
#include	<conv.h>
#include	<debug.h>
#include	<phdr_msg.h>


/*
 * Program headers
 */



/*
 * 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 dynamic section */
	PHDR_CMD_T_DUMP =	0,	/* phdr:dump */

	/* Commands that correspond directly to program header fields */
	PHDR_CMD_T_P_TYPE =	1,	/* phdr:p_type */
	PHDR_CMD_T_P_OFFSET =	2,	/* phdr:p_offset */
	PHDR_CMD_T_P_VADDR =	3,	/* phdr:p_vaddr */
	PHDR_CMD_T_P_PADDR =	4,	/* phdr:p_paddr */
	PHDR_CMD_T_P_FILESZ =	5,	/* phdr:p_filesz */
	PHDR_CMD_T_P_MEMSZ =	6,	/* phdr:p_memsz */
	PHDR_CMD_T_P_FLAGS =	7,	/* phdr:p_flags */
	PHDR_CMD_T_P_ALIGN =	8,	/* phdr:p_align */

	/* Commands that do not correspond directly to a specific phdr tag */
	PHDR_CMD_T_INTERP =	9,	/* phdr:interp */
	PHDR_CMD_T_DELETE =	10,	/* phdr:delete */
	PHDR_CMD_T_MOVE =	11	/* phdr:move */
} PHDR_CMD_T;



/*
 * The following type is ued by locate_interp() to return
 * information about the interpreter program header.
 */
typedef struct {
	Word			phndx;	/* Index of PT_INTERP header */
	Phdr			*phdr;		/* PT_INTERP header */
	elfedit_section_t	*sec;		/* Section containing string */
	Word			stroff;		/* Offset into string section */
	const char		*str;		/* Interpreter string */
} INTERP_STATE;


#ifndef _ELF64
/*
 * We supply this function for the msg module
 */
const char *
_phdr_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 phdr_opt_t enum specifies a bit value for every optional
 * argument allowed by a command in this module.
 */
typedef enum {
	PHDR_OPT_F_AND =	1,	/* -and: AND (&) values to dest */
	PHDR_OPT_F_CMP =	2,	/* -cmp: Complement (~) values */
	PHDR_OPT_F_PHNDX =	4,	/* -phndx: Program header by index, */
					/*	not by name */
	PHDR_OPT_F_OR =		8	/* -or: OR (|) values to dest */
} phdr_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;
	phdr_opt_t		optmask;   	/* Mask of options used */
	int			argc;		/* # of plain arguments */
	const char		**argv;		/* Plain arguments */
	int			ndx_set;	/* True if ndx is valid */
	Word			ndx;		/* Index of header if cmd */
						/*	accepts it */
	int			print_req;	/* Call is a print request */
} ARGSTATE;


/*
 * Standard argument processing for phdr module
 *
 * entry
 *	obj_state, argc, argv - Standard command arguments
 *	optmask - Mask of allowed optional arguments.
 *	cmd - PHDR_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[],
    PHDR_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 PHDR_CMD_T_DUMP:
		if (argc > 1)
			elfedit_command_usage();
		argstate->print_req = 1;
		break;
	case PHDR_CMD_T_P_FLAGS:
		/* phdr:sh_flags allows an arbitrary number of arguments */
		argstate->print_req = (argc < 2);
		break;
	case PHDR_CMD_T_INTERP:
		if (argc > 1)
			elfedit_command_usage();
		argstate->print_req = (argc == 0);
		break;
	case PHDR_CMD_T_DELETE:
		if ((argc < 1) || (argc > 2))
			elfedit_command_usage();
		argstate->print_req = 0;
		break;
	case PHDR_CMD_T_MOVE:
		if ((argc < 2) || (argc > 3))
			elfedit_command_usage();
		argstate->print_req = 0;
		break;

	default:
		/* The remaining commands accept 2 plain arguments */
		if (argc > 2)
			elfedit_command_usage();
		argstate->print_req = (argc < 2);
		break;
	}

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

	argstate->ndx_set = 0;
	if ((argc > 0) && (cmd != PHDR_CMD_T_INTERP)) {
		/*
		 * If the -phndx option is present, the first argument is
		 * the index of the header to use. Otherwise, it is a
		 * name corresponding to its type, similar to the way
		 * elfdump works with its -N option.
		 */
		if (argstate->optmask & PHDR_OPT_F_PHNDX) {
			argstate->ndx = (Word) elfedit_atoui_range(
			    argstate->argv[0], MSG_ORIG(MSG_STR_ELEMENT), 0,
			    argstate->obj_state->os_phnum - 1, NULL);
			argstate->ndx_set = 1;
		} else {
			Conv_inv_buf_t inv_buf;
			Ehdr		*ehdr = obj_state->os_ehdr;
			Half		mach = ehdr->e_machine;
			uchar_t		osabi = ehdr->e_ident[EI_OSABI];
			Word		i;
			Phdr		*phdr;

			argstate->ndx = (Word) elfedit_atoconst(
			    argstate->argv[0], ELFEDIT_CONST_PT);
			phdr = obj_state->os_phdr;
			for (i = 0; i < obj_state->os_phnum; i++, phdr++) {
				if (phdr->p_type == argstate->ndx) {
					argstate->ndx = i;
					argstate->ndx_set = 1;
					elfedit_msg(ELFEDIT_MSG_DEBUG,
					    MSG_INTL(MSG_DEBUG_PHDR),
					    EC_WORD(i), conv_phdr_type(osabi,
					    mach, phdr->p_type, 0, &inv_buf));
					break;
				}
			}
			if (i == argstate->obj_state->os_phnum)
				elfedit_msg(ELFEDIT_MSG_ERR,
				    MSG_INTL(MSG_ERR_NOPHDR), conv_phdr_type(
				    osabi, mach, argstate->ndx, 0, &inv_buf));
		}
	}

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

}



/*
 * Locate the interpreter string for the object and related information
 *
 * entry:
 *	obj_state - Object state
 *	interp - NULL, or variable to be filled in with information
 *		about the interpteter string.
 */
static const char *
locate_interp(elfedit_obj_state_t *obj_state, INTERP_STATE *interp)
{
	INTERP_STATE		local_interp;
	elfedit_section_t	*strsec;	/* String table */
	size_t		phnum;		/* # of program headers */
	int		phndx;		/* Index of PT_INTERP program header */
	Phdr		*phdr;		/* Program header array */
	Word		i;

	if (interp == NULL)
		interp = &local_interp;

	/* Locate the PT_INTERP program header */
	phnum = obj_state->os_phnum;
	phdr = obj_state->os_phdr;

	for (phndx = 0; phndx < phnum; phndx++) {
		if (phdr[phndx].p_type  == PT_INTERP) {
			interp->phndx = phndx;
			interp->phdr = phdr + phndx;
			break;
		}
	}
	/* If no PT_INTERP program header found, we cannot proceed */
	if (phndx == phnum)
		elfedit_elferr(obj_state->os_file,
		    MSG_INTL(MSG_ERR_NOINTERPPHDR));

	/*
	 * Locate the section containing the interpteter string as well
	 * as the string itself.
	 *
	 * The program header contains a direct offset to the string, so
	 * we find the section by walking through the them looking for
	 * the one with a base and size that would contain the string.
	 * Note that this target section cannot be in a NOBITS section.
	 */
	for (i = 1; i < obj_state->os_shnum; i++) {
		strsec = &obj_state->os_secarr[i];

		if ((strsec->sec_shdr->sh_type != SHT_NOBITS) &&
		    (interp->phdr->p_offset >= strsec->sec_shdr->sh_offset) &&
		    ((interp->phdr->p_offset + interp->phdr->p_filesz) <=
		    (strsec->sec_shdr->sh_offset +
		    strsec->sec_shdr->sh_size))) {
			interp->sec = strsec;

			interp->stroff = interp->phdr->p_offset -
			    strsec->sec_shdr->sh_offset;
			interp->str = ((char *)strsec->sec_data->d_buf) +
			    interp->stroff;
			return (interp->str);
		}
	}

	/*
	 * We don't expect to get here: If there is a PT_INTERP header,
	 * we fully expect the string to exist.
	 */
	elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOINTERPSEC));
	/*NOTREACHED*/

	return (NULL);		/* For lint */
}

/*
 * Print program 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 - PHDR_CMD_T_* value giving identify of caller
 *	argstate - State block for section header array. The following
 *		fields are examined in order to determine the form
 *		of output: ndx_set, ndx, print_req.
 */
static void
print_phdr(PHDR_CMD_T cmd, int autoprint, ARGSTATE *argstate)
{
	elfedit_outstyle_t	outstyle;
	Ehdr			*ehdr = argstate->obj_state->os_ehdr;
	uchar_t			osabi = ehdr->e_ident[EI_OSABI];
	Half			mach = ehdr->e_machine;
	Word			ndx, cnt, by_type, type;
	Phdr			*phdr;

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

	/*
	 * Determine which indexes to display:
	 *
	 * -	If the user specified an index, the display starts
	 *	with that item. If it was a print_request, and the
	 *	index was specified by type, then all items of the
	 *	same type are shown. If not a print request, or the index
	 *	was given numerically, then just the single item is shown.
	 *
	 * -	If no index is specified, every program header is shown.
	 */
	by_type = 0;
	if (argstate->ndx_set) {
		ndx = argstate->ndx;
		if (argstate->print_req &&
		    ((argstate->optmask & PHDR_OPT_F_PHNDX) == 0)) {
			by_type = 1;
			type = argstate->obj_state->os_phdr[ndx].p_type;
			cnt = argstate->obj_state->os_phnum - ndx;
		} else {
			cnt = 1;
		}
	} else {
		ndx = 0;
		cnt = argstate->obj_state->os_phnum;
	}
	phdr = argstate->obj_state->os_phdr + ndx;

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

	/*
	 * If doing default output, use elfdump style where we
	 * show all program header attributes. In this case, the
	 * command that called us doesn't matter.
	 *
	 * Exclude PHDR_CMD_T_INTERP from this: It isn't per-phdr like
	 * the other commands.
	 */
	if ((outstyle == ELFEDIT_OUTSTYLE_DEFAULT) &&
	    (cmd != PHDR_CMD_T_INTERP)) {
		for (; cnt--; ndx++, phdr++) {
			if (by_type && (type != phdr->p_type))
				continue;

			elfedit_printf(MSG_ORIG(MSG_STR_NL));
			elfedit_printf(MSG_INTL(MSG_ELF_PHDR), EC_WORD(ndx));
			Elf_phdr(0, osabi, mach, phdr);
		}
		return;
	}

	if (cmd == PHDR_CMD_T_INTERP) {
		INTERP_STATE interp;

		(void) locate_interp(argstate->obj_state, &interp);
		switch (outstyle) {
		case ELFEDIT_OUTSTYLE_DEFAULT:
			elfedit_printf(MSG_INTL(MSG_FMT_ELF_INTERP),
			    interp.sec->sec_name, interp.str);
			break;
		case ELFEDIT_OUTSTYLE_SIMPLE:
			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), interp.str);
			break;
		case ELFEDIT_OUTSTYLE_NUM:
			elfedit_printf(MSG_ORIG(MSG_FMT_U_NL),
			    EC_WORD(interp.stroff));
			break;
		}
		return;
	}

	/* Handle the remaining commands */
	for (; cnt--; ndx++, phdr++) {
		if (by_type && (type != phdr->p_type))
			continue;

		switch (cmd) {
		case PHDR_CMD_T_P_TYPE:
			if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
				Conv_inv_buf_t inv_buf;

				elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
				    conv_phdr_type(osabi,
				    argstate->obj_state->os_ehdr->e_machine,
				    phdr->p_type, 0, &inv_buf));
			} else {
				elfedit_printf(MSG_ORIG(MSG_FMT_X_NL),
				    EC_WORD(phdr->p_type));
			}
			break;

		case PHDR_CMD_T_P_OFFSET:
			elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL),
			    EC_OFF(phdr->p_offset));
			break;

		case PHDR_CMD_T_P_VADDR:
			elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL),
			    EC_ADDR(phdr->p_vaddr));
			break;

		case PHDR_CMD_T_P_PADDR:
			elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL),
			    EC_ADDR(phdr->p_paddr));
			break;

		case PHDR_CMD_T_P_FILESZ:
			elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL),
			    EC_XWORD(phdr->p_filesz));
			break;

		case PHDR_CMD_T_P_MEMSZ:
			elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL),
			    EC_XWORD(phdr->p_memsz));
			break;

		case PHDR_CMD_T_P_FLAGS:
			if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
				Conv_phdr_flags_buf_t phdr_flags_buf;

				elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
				    conv_phdr_flags(osabi, phdr->p_flags,
				    CONV_FMT_NOBKT, &phdr_flags_buf));
			} else {
				elfedit_printf(MSG_ORIG(MSG_FMT_X_NL),
				    EC_WORD(phdr->p_flags));
			}
			break;

		case PHDR_CMD_T_P_ALIGN:
			elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL),
			    EC_XWORD(phdr->p_align));
			break;
		}
	}
}


/*
 * Called from cmd_body() in the case where a plain argument
 * is given to phdr:interp to change the interpreter.
 */
static elfedit_cmdret_t
cmd_body_set_interp(ARGSTATE *argstate)
{
	elfedit_obj_state_t	*obj_state = argstate->obj_state;
	elfedit_section_t	*strsec;	/* String table */
	INTERP_STATE	interp;
	Word		numdyn;		/* # of elements in dyn arr */
	size_t		phnum;		/* # of program headers */
	Phdr		*phdr;		/* Program header array */
	Word		i, j;
	Word		str_offset;	/* Offset in strsec to new interp str */
	int		str_found = 0;	 /* True when we have new interp str */
	Word		str_size;	/* Size of new interp string + NULL */

	phnum = obj_state->os_phnum;
	phdr = obj_state->os_phdr;

	/* Locate the PT_INTERP program header */
	(void) locate_interp(obj_state, &interp);
	strsec = interp.sec;
	str_offset = interp.stroff;

	/*
	 * If the given string is the same as the existing interpreter
	 * string, say so and return.
	 */
	if (strcmp(interp.str, argstate->argv[0]) == 0) {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_OLDINTERPOK),
		    EC_WORD(strsec->sec_shndx), strsec->sec_name,
		    EC_WORD(str_offset), interp.str);
		return (ELFEDIT_CMDRET_NONE);
	}

	/*
	 * An ELF PT_INTERP usually references its own special section
	 * instead of some other string table. The ELF ABI says that this
	 * section must be named ".interp". Hence, this is a rare case
	 * in which the name of a section can be taken as an indication
	 * of its contents. .interp is typically sized to just fit
	 * the original string, including its NULL termination. You can
	 * treat it as a string table with one string.
	 *
	 * Thanks to 'elfedit', it may be that we encounter a file where
	 * PT_INTERP does not reference the .interp section. This will happen
	 * if elfedit is used to change the interpreter to a string that is
	 * too big to fit in .interp, in which case we will use the
	 * .dynstr string table (That code is below, in this function).
	 *
	 * Given the above facts, our next step is to locate the .interp
	 * section and see if our new string will fit in it. Since we can't
	 * depend on PT_INTERP, we search the section headers to find a
	 * section whith the following characteristics:
	 *	- The name is ".interp".
	 *	- Section is allocable (SHF_ALLOC) and SHT_PROGBITS.
	 *	- It is not part of a writable segment.
	 * If we find such a section, and the new string fits, we will
	 * write it there.
	 */
	str_size = strlen(argstate->argv[0]) + 1;
	for (i = 1; i < obj_state->os_shnum; i++) {
		strsec = &obj_state->os_secarr[i];
		if ((strcmp(strsec->sec_name, MSG_ORIG(MSG_SEC_INTERP)) == 0) &&
		    (strsec->sec_shdr->sh_flags & SHF_ALLOC) &&
		    (strsec->sec_shdr->sh_type & SHT_PROGBITS)) {
			for (j = 0; j < phnum; j++) {
				Phdr *tphdr = &phdr[j];
				if ((strsec->sec_shdr->sh_offset >=
				    tphdr->p_offset) &&
				    ((strsec->sec_shdr->sh_offset +
				    strsec->sec_shdr->sh_size) <=
				    (tphdr->p_offset + tphdr->p_filesz)) &&
				    (tphdr->p_flags & PF_W)) {
					break;
				}
			}
			if ((j == phnum) &&
			    (str_size <= strsec->sec_shdr->sh_size)) {
				/* .interp section found, and has room */
				str_found = 1;
				str_offset = 0;
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_NEWISTR), EC_WORD(j),
				    strsec->sec_name, EC_WORD(str_offset),
				    argstate->argv[0]);
				/* Put new value in section */
				(void) strncpy((char *)strsec->sec_data->d_buf,
				    argstate->argv[0],
				    strsec->sec_shdr->sh_size);
				/* Set libelf dirty bit so change is flushed */
				elfedit_modified_data(strsec);
				break;
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LNGISTR), EC_WORD(j),
				    strsec->sec_name, EC_WORD(str_offset),
				    EC_WORD(str_size),
				    EC_WORD(strsec->sec_shdr->sh_size),
				    argstate->argv[0]);
			}
		}
	}

	/*
	 * If the above did not find a string within the .interp section,
	 * then we have a second option. If this ELF object has a dynamic
	 * section, then we are willing to use strings from within the
	 * associated .dynstr string table. And if there is reserved space
	 * in .dynstr (as reported by the DT_SUNW_STRPAD dynamic entry),
	 * then we are even willing to add a new string to .dynstr.
	 */
	if (!str_found) {
		elfedit_section_t	*dynsec;
		Dyn			*dyn;

		dynsec = elfedit_sec_getdyn(obj_state, &dyn, &numdyn);
		strsec = elfedit_sec_getstr(obj_state,
		    dynsec->sec_shdr->sh_link, 0);

		/* Does string exist in the table already, or can we add it? */
		str_offset = elfedit_strtab_insert(obj_state, strsec,
		    dynsec, argstate->argv[0]);
	}


	/*
	 * If we are here, we know we have a replacement string, because
	 * the errors from checking .dynamic/.dynstr will not allow
	 * things to get here otherwise.
	 *
	 * The PT_INTERP program header references the string directly,
	 * so we add the section offset to the string offset.
	 */
	interp.phdr->p_offset = strsec->sec_shdr->sh_offset + str_offset;
	interp.phdr->p_filesz = str_size;
	elfedit_modified_phdr(obj_state);
	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_SETPHINTERP),
	    EC_WORD(interp.phndx), EC_XWORD(interp.phdr->p_offset),
	    EC_XWORD(interp.phdr->p_filesz));

	return (ELFEDIT_CMDRET_MOD);
}


/*
 * Common body for the phdr: 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 PHDR_CMD_T_* constants listed above, specifying
 *		which command to implement.
 *	obj_state, argc, argv - Standard command arguments
 */
static elfedit_cmdret_t
cmd_body(PHDR_CMD_T cmd, elfedit_obj_state_t *obj_state,
    int argc, const char *argv[])
{
	ARGSTATE		argstate;
	Phdr			*phdr;
	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
	int			do_autoprint = 1;

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

	/* If this is a printing request, print and return */
	if (argstate.print_req) {
		print_phdr(cmd, 0, &argstate);
		return (ELFEDIT_CMDRET_NONE);
	}


	if (argstate.ndx_set)
		phdr = &argstate.obj_state->os_phdr[argstate.ndx];

	switch (cmd) {
		/*
		 * PHDR_CMD_T_DUMP can't get here: It never has more than
		 * one argument, and is handled above.
		 */

	case PHDR_CMD_T_P_TYPE:
		{
			Ehdr	*ehdr = obj_state->os_ehdr;
			uchar_t	osabi = ehdr->e_ident[EI_OSABI];
			Half	mach = ehdr->e_machine;
			Word p_type = elfedit_atoconst(argstate.argv[1],
			    ELFEDIT_CONST_PT);
			Conv_inv_buf_t inv_buf1, inv_buf2;

			if (phdr->p_type == p_type) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_S_OK),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_TYPE),
				    conv_phdr_type(osabi, mach, phdr->p_type,
				    0, &inv_buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_S_CHG),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_TYPE),
				    conv_phdr_type(osabi, mach,
				    phdr->p_type, 0, &inv_buf1),
				    conv_phdr_type(osabi, mach,
				    p_type, 0, &inv_buf2));
				ret = ELFEDIT_CMDRET_MOD;
				phdr->p_type = p_type;
			}
		}
		break;

	case PHDR_CMD_T_P_OFFSET:
		{
			Off p_offset;

			p_offset = elfedit_atoui(argstate.argv[1], NULL);
			if (phdr->p_offset == p_offset) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_OFFSET),
				    EC_XWORD(phdr->p_offset));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_OFFSET),
				    EC_XWORD(phdr->p_offset),
				    EC_XWORD(p_offset));
				ret = ELFEDIT_CMDRET_MOD;
				phdr->p_offset = p_offset;
			}
		}
		break;

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

			if (phdr->p_vaddr == p_vaddr) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_VADDR),
				    EC_ADDR(phdr->p_vaddr));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_VADDR),
				    EC_ADDR(phdr->p_vaddr), EC_ADDR(p_vaddr));
				ret = ELFEDIT_CMDRET_MOD;
				phdr->p_vaddr = p_vaddr;
			}
		}
		break;

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

			if (phdr->p_paddr == p_paddr) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_PADDR),
				    EC_ADDR(phdr->p_paddr));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_PADDR),
				    EC_ADDR(phdr->p_paddr), EC_ADDR(p_paddr));
				ret = ELFEDIT_CMDRET_MOD;
				phdr->p_paddr = p_paddr;
			}
		}
		break;

	case PHDR_CMD_T_P_FILESZ:
		{
			Xword p_filesz = elfedit_atoui(argstate.argv[1], NULL);

			if (phdr->p_filesz == p_filesz) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_FILESZ),
				    EC_XWORD(phdr->p_filesz));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_FILESZ),
				    EC_XWORD(phdr->p_filesz),
				    EC_XWORD(p_filesz));
				ret = ELFEDIT_CMDRET_MOD;
				phdr->p_filesz = p_filesz;
			}
		}
		break;

	case PHDR_CMD_T_P_MEMSZ:
		{
			Xword p_memsz = elfedit_atoui(argstate.argv[1], NULL);

			if (phdr->p_memsz == p_memsz) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_MEMSZ),
				    EC_XWORD(phdr->p_memsz));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_MEMSZ),
				    EC_XWORD(phdr->p_memsz),
				    EC_XWORD(p_memsz));
				ret = ELFEDIT_CMDRET_MOD;
				phdr->p_memsz = p_memsz;
			}
		}
		break;

	case PHDR_CMD_T_P_FLAGS:
		{
			Ehdr	*ehdr = obj_state->os_ehdr;
			uchar_t	osabi = ehdr->e_ident[EI_OSABI];
			Conv_phdr_flags_buf_t buf1, buf2;
			Word	p_flags = 0;
			int	i;

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

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

			/* Perform any requested bit operations */
			if (argstate.optmask & PHDR_OPT_F_AND)
				p_flags &= phdr->p_flags;
			else if (argstate.optmask & PHDR_OPT_F_OR)
				p_flags |= phdr->p_flags;

			/* Set the value */
			if (phdr->p_flags == p_flags) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_S_OK),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_FLAGS),
				    conv_phdr_flags(osabi, phdr->p_flags,
				    0, &buf1));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_S_CHG),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_FLAGS),
				    conv_phdr_flags(osabi, phdr->p_flags,
				    0, &buf1),
				    conv_phdr_flags(osabi, p_flags, 0, &buf2));
				ret = ELFEDIT_CMDRET_MOD;
				phdr->p_flags = p_flags;
			}
		}
		break;

	case PHDR_CMD_T_P_ALIGN:
		{
			Xword p_align = elfedit_atoui(argstate.argv[1], NULL);

			if (phdr->p_align == p_align) {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_OK),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_ALIGN),
				    EC_XWORD(phdr->p_align));
			} else {
				elfedit_msg(ELFEDIT_MSG_DEBUG,
				    MSG_INTL(MSG_DEBUG_LLX_CHG),
				    argstate.ndx, MSG_ORIG(MSG_CMD_P_ALIGN),
				    EC_XWORD(phdr->p_align),
				    EC_XWORD(p_align));
				ret = ELFEDIT_CMDRET_MOD;
				phdr->p_align = p_align;
			}
		}
		break;

	case PHDR_CMD_T_INTERP:
		ret = cmd_body_set_interp(&argstate);
		break;

	case PHDR_CMD_T_DELETE:
		{
			Word cnt = (argstate.argc == 1) ? 1 :
			    (Word) elfedit_atoui_range(argstate.argv[1],
			    MSG_ORIG(MSG_STR_COUNT), 1,
			    obj_state->os_phnum - argstate.ndx, NULL);

			elfedit_array_elts_delete(MSG_ORIG(MSG_MOD_NAME),
			    obj_state->os_phdr, sizeof (Phdr),
			    obj_state->os_phnum, argstate.ndx, cnt);
			do_autoprint = 0;
			ret = ELFEDIT_CMDRET_MOD;
		}
		break;

	case PHDR_CMD_T_MOVE:
		{
			Phdr	save;
			Word	cnt;
			Word	dstndx;

			do_autoprint = 0;
			dstndx = (Word)
			    elfedit_atoui_range(argstate.argv[1],
			    MSG_ORIG(MSG_STR_DST_INDEX), 0,
			    obj_state->os_phnum - 1, NULL);
			if (argstate.argc == 2) {
				cnt = 1;
			} else {
				cnt = (Word) elfedit_atoui_range(
				    argstate.argv[2], MSG_ORIG(MSG_STR_COUNT),
				    1, obj_state->os_phnum, NULL);
			}
			elfedit_array_elts_move(MSG_ORIG(MSG_MOD_NAME),
			    obj_state->os_phdr, sizeof (save),
			    obj_state->os_phnum, argstate.ndx, dstndx,
			    cnt, &save);
			ret = ELFEDIT_CMDRET_MOD;
		}
		break;
	}

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

	/* Do autoprint */
	if (do_autoprint)
		print_phdr(cmd, 1, &argstate);

	return (ret);
}



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

/*
 * A number of the commands accept a PT_ constant as their first
 * argument as long as the -phndx option is not used.
 */
/*ARGSUSED*/
static void
cpl_1starg_pt(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	int i;

	for (i = 0; i < num_opt; i++)
		if (strcmp(MSG_ORIG(MSG_STR_MINUS_PHNDX), argv[i]) == 0)
			return;

	if (argc == (num_opt + 1))
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_PT);
}

/*ARGSUSED*/
static void
cpl_p_type(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/* The first argument follows the standard rules */
	cpl_1starg_pt(obj_state, cpldata, argc, argv, num_opt);

	/* The second argument can be a PT_ value */
	if (argc == (num_opt + 2))
		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_PT);
}


/*ARGSUSED*/
static void
cpl_p_flags(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
	/* The first argument follows the standard rules */
	cpl_1starg_pt(obj_state, cpldata, argc, argv, num_opt);

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



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

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

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

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

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

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

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

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

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

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

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

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


/*ARGSUSED*/
elfedit_module_t *
elfedit_init(elfedit_module_version_t version)
{
	/* Multiple commands accept a 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_PHNDX),
		    /* MSG_INTL(MSG_OPTDESC_PHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_PHNDX), 0,
		    PHDR_OPT_F_PHNDX, 0 },
		{ NULL }
	};

	/* For commands that only accept -phndx */
	static elfedit_cmd_optarg_t opt_minus_phndx[] = {
		{ MSG_ORIG(MSG_STR_MINUS_PHNDX),
		    /* MSG_INTL(MSG_OPTDESC_PHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_PHNDX), 0,
		    PHDR_OPT_F_PHNDX, 0 },
		{ NULL }
	};


	/* phdr: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 arg_dump[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* phdr:p_type */
	static const char *name_p_type[] = { MSG_ORIG(MSG_CMD_P_TYPE), NULL };
	static elfedit_cmd_optarg_t arg_p_type[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_TYPE),
		    /* MSG_INTL(MSG_A2_P_TYPE_TYPE) */
		    ELFEDIT_I18NHDL(MSG_A2_P_TYPE_TYPE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* phdr:p_offset */
	static const char *name_p_offset[] = { MSG_ORIG(MSG_CMD_P_OFFSET),
	    NULL };
	static elfedit_cmd_optarg_t arg_p_offset[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_P_OFFSET_VALUE) */
		    ELFEDIT_I18NHDL(MSG_A2_P_OFFSET_VALUE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* phdr:p_vaddr */
	static const char *name_p_vaddr[] = { MSG_ORIG(MSG_CMD_P_VADDR),
	    NULL };
	static elfedit_cmd_optarg_t arg_p_vaddr[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_ADDR),
		    /* MSG_INTL(MSG_A2_P_VADDR_ADDR) */
		    ELFEDIT_I18NHDL(MSG_A2_P_VADDR_ADDR),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* phdr:p_paddr */
	static const char *name_p_paddr[] = { MSG_ORIG(MSG_CMD_P_PADDR),
	    NULL };
	static elfedit_cmd_optarg_t arg_p_paddr[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_ADDR),
		    /* MSG_INTL(MSG_A2_P_PADDR_ADDR) */
		    ELFEDIT_I18NHDL(MSG_A2_P_PADDR_ADDR),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* phdr:p_filesz */
	static const char *name_p_filesz[] = { MSG_ORIG(MSG_CMD_P_FILESZ),
	    NULL };
	static elfedit_cmd_optarg_t arg_p_filesz[] = {
	    /* MSG_INTL(MSG_A1_ELEMENT) */
		{ MSG_ORIG(MSG_STR_ELEMENT), ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_SIZE),
		    /* MSG_INTL(MSG_A2_P_FILESZ_SIZE) */
		    ELFEDIT_I18NHDL(MSG_A2_P_FILESZ_SIZE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* phdr:p_memsz */
	static const char *name_p_memsz[] = { MSG_ORIG(MSG_CMD_P_MEMSZ),
	    NULL };
	static elfedit_cmd_optarg_t arg_p_memsz[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_SIZE),
		    /* MSG_INTL(MSG_A2_P_MEMSZ_SIZE) */
		    ELFEDIT_I18NHDL(MSG_A2_P_MEMSZ_SIZE),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* shdr:p_flags */
	static const char *name_p_flags[] = {
	    MSG_ORIG(MSG_CMD_P_FLAGS), NULL };
	static elfedit_cmd_optarg_t opt_p_flags[] = {
		{ ELFEDIT_STDOA_OPT_AND, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, PHDR_OPT_F_AND, PHDR_OPT_F_OR },
		{ ELFEDIT_STDOA_OPT_CMP, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, PHDR_OPT_F_CMP, 0 },
		{ MSG_ORIG(MSG_STR_MINUS_PHNDX),
		    /* MSG_INTL(MSG_OPTDESC_PHNDX) */
		    ELFEDIT_I18NHDL(MSG_OPTDESC_PHNDX), 0,
		    PHDR_OPT_F_PHNDX, 0 },
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ ELFEDIT_STDOA_OPT_OR, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, PHDR_OPT_F_OR, PHDR_OPT_F_AND },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_p_flags[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_VALUE),
		    /* MSG_INTL(MSG_A2_P_FLAGS_VALUE) */
		    ELFEDIT_I18NHDL(MSG_A2_P_FLAGS_VALUE),
		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
		{ NULL }
	};

	/* phdr:p_align */
	static const char *name_p_align[] = { MSG_ORIG(MSG_CMD_P_ALIGN),
	    NULL };
	static elfedit_cmd_optarg_t arg_p_align[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_ALIGN),
		    /* MSG_INTL(MSG_A2_P_ALIGN_ALIGN) */
		    ELFEDIT_I18NHDL(MSG_A2_P_ALIGN_ALIGN),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* phdr:interp */
	static const char *name_interp[] = { MSG_ORIG(MSG_CMD_INTERP), NULL };
	static elfedit_cmd_optarg_t opt_interp[] = {
		{ ELFEDIT_STDOA_OPT_O, NULL,
		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
		{ NULL }
	};
	static elfedit_cmd_optarg_t arg_interp[] = {
		{ MSG_ORIG(MSG_STR_NEWPATH),
		    /* MSG_INTL(MSG_A1_INTERP_NEWPATH) */
		    ELFEDIT_I18NHDL(MSG_A1_INTERP_NEWPATH),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* phdr:delete */
	static const char *name_delete[] = { MSG_ORIG(MSG_CMD_DELETE), NULL };
	static elfedit_cmd_optarg_t arg_delete[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    0 },
		{ MSG_ORIG(MSG_STR_COUNT),
		    /* MSG_INTL(MSG_A2_DELETE_COUNT) */
		    ELFEDIT_I18NHDL(MSG_A2_DELETE_COUNT),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	/* phdr:move */
	static const char *name_move[] = { MSG_ORIG(MSG_CMD_MOVE), NULL };
	static elfedit_cmd_optarg_t arg_move[] = {
		{ MSG_ORIG(MSG_STR_ELEMENT),
		    /* MSG_INTL(MSG_A1_ELEMENT) */
		    ELFEDIT_I18NHDL(MSG_A1_ELEMENT),
		    ELFEDIT_CMDOA_F_OPT },
		{ MSG_ORIG(MSG_STR_DST_INDEX),
		    /* MSG_INTL(MSG_A2_MOVE_DST_INDEX) */
		    ELFEDIT_I18NHDL(MSG_A2_MOVE_DST_INDEX),
		    0 },
		{ MSG_ORIG(MSG_STR_COUNT),
		    /* MSG_INTL(MSG_A3_MOVE_COUNT) */
		    ELFEDIT_I18NHDL(MSG_A3_MOVE_COUNT),
		    ELFEDIT_CMDOA_F_OPT },
		{ NULL }
	};

	static elfedit_cmd_t cmds[] = {
		/* phdr:dump */
		{ cmd_dump, cpl_1starg_pt, name_dump,
		    /* MSG_INTL(MSG_DESC_DUMP) */
		    ELFEDIT_I18NHDL(MSG_DESC_DUMP),
		    /* MSG_INTL(MSG_HELP_DUMP) */
		    ELFEDIT_I18NHDL(MSG_HELP_DUMP),
		    opt_minus_phndx, arg_dump },

		/* phdr:p_type */
		{ cmd_p_type, cpl_p_type, name_p_type,
		    /* MSG_INTL(MSG_DESC_P_TYPE) */
		    ELFEDIT_I18NHDL(MSG_DESC_P_TYPE),
		    /* MSG_INTL(MSG_HELP_P_TYPE) */
		    ELFEDIT_I18NHDL(MSG_HELP_P_TYPE),
		    opt_std, arg_p_type },

		/* phdr:p_offset */
		{ cmd_p_offset, cpl_1starg_pt, name_p_offset,
		    /* MSG_INTL(MSG_DESC_P_OFFSET) */
		    ELFEDIT_I18NHDL(MSG_DESC_P_OFFSET),
		    /* MSG_INTL(MSG_HELP_P_OFFSET) */
		    ELFEDIT_I18NHDL(MSG_HELP_P_OFFSET),
		    opt_std, arg_p_offset },

		/* phdr:p_vaddr */
		{ cmd_p_vaddr, cpl_1starg_pt, name_p_vaddr,
		    /* MSG_INTL(MSG_DESC_P_VADDR) */
		    ELFEDIT_I18NHDL(MSG_DESC_P_VADDR),
		    /* MSG_INTL(MSG_HELP_P_VADDR) */
		    ELFEDIT_I18NHDL(MSG_HELP_P_VADDR),
		    opt_std, arg_p_vaddr },

		/* phdr:p_paddr */
		{ cmd_p_paddr, cpl_1starg_pt, name_p_paddr,
		    /* MSG_INTL(MSG_DESC_P_PADDR) */
		    ELFEDIT_I18NHDL(MSG_DESC_P_PADDR),
		    /* MSG_INTL(MSG_HELP_P_PADDR) */
		    ELFEDIT_I18NHDL(MSG_HELP_P_PADDR),
		    opt_std, arg_p_paddr },

		/* phdr:p_filesz */
		{ cmd_p_filesz, cpl_1starg_pt, name_p_filesz,
		    /* MSG_INTL(MSG_DESC_P_FILESZ) */
		    ELFEDIT_I18NHDL(MSG_DESC_P_FILESZ),
		    /* MSG_INTL(MSG_HELP_P_FILESZ) */
		    ELFEDIT_I18NHDL(MSG_HELP_P_FILESZ),
		    opt_std, arg_p_filesz },

		/* phdr:p_memsz */
		{ cmd_p_memsz, cpl_1starg_pt, name_p_memsz,
		    /* MSG_INTL(MSG_DESC_P_MEMSZ) */
		    ELFEDIT_I18NHDL(MSG_DESC_P_MEMSZ),
		    /* MSG_INTL(MSG_HELP_P_MEMSZ) */
		    ELFEDIT_I18NHDL(MSG_HELP_P_MEMSZ),
		    opt_std, arg_p_memsz },

		/* phdr:p_flags */
		{ cmd_p_flags, cpl_p_flags, name_p_flags,
		    /* MSG_INTL(MSG_DESC_P_FLAGS) */
		    ELFEDIT_I18NHDL(MSG_DESC_P_FLAGS),
		    /* MSG_INTL(MSG_HELP_P_FLAGS) */
		    ELFEDIT_I18NHDL(MSG_HELP_P_FLAGS),
		    opt_p_flags, arg_p_flags },

		/* phdr:p_align */
		{ cmd_p_align, cpl_1starg_pt, name_p_align,
		    /* MSG_INTL(MSG_DESC_P_ALIGN) */
		    ELFEDIT_I18NHDL(MSG_DESC_P_ALIGN),
		    /* MSG_INTL(MSG_HELP_P_ALIGN) */
		    ELFEDIT_I18NHDL(MSG_HELP_P_ALIGN),
		    opt_std, arg_p_align },

		/* phdr:interp */
		{ cmd_interp, NULL, name_interp,
		    /* MSG_INTL(MSG_DESC_INTERP) */
		    ELFEDIT_I18NHDL(MSG_DESC_INTERP),
		    /* MSG_INTL(MSG_HELP_INTERP) */
		    ELFEDIT_I18NHDL(MSG_HELP_INTERP),
		    opt_interp, arg_interp },

		/* phdr:delete */
		{ cmd_delete, cpl_1starg_pt, name_delete,
		    /* MSG_INTL(MSG_DESC_DELETE) */
		    ELFEDIT_I18NHDL(MSG_DESC_DELETE),
		    /* MSG_INTL(MSG_HELP_DELETE) */
		    ELFEDIT_I18NHDL(MSG_HELP_DELETE),
		    opt_minus_phndx, arg_delete },

		/* phdr:move */
		{ cmd_move, cpl_1starg_pt, name_move,
		    /* MSG_INTL(MSG_DESC_MOVE) */
		    ELFEDIT_I18NHDL(MSG_DESC_MOVE),
		    /* MSG_INTL(MSG_HELP_MOVE) */
		    ELFEDIT_I18NHDL(MSG_HELP_MOVE),
		    opt_minus_phndx, arg_move },

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