Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include	<stdio.h>
     28 #include	<ctype.h>
     29 #include	<unistd.h>
     30 #include	<elfedit.h>
     31 #include	<strings.h>
     32 #include	<debug.h>
     33 #include	<conv.h>
     34 #include	<str_msg.h>
     35 
     36 
     37 
     38 
     39 #define	MAXNDXSIZE	10
     40 
     41 
     42 
     43 /*
     44  * This module uses shared code for several of the commands.
     45  * It is sometimes necessary to know which specific command
     46  * is active.
     47  */
     48 typedef enum {
     49 	STR_CMD_T_DUMP =	0,	/* str:dump */
     50 	STR_CMD_T_SET =		1,	/* str:set */
     51 	STR_CMD_T_ADD =		2,	/* str:add */
     52 	STR_CMD_T_ZERO =	3,	/* str:zero */
     53 } STR_CMD_T;
     54 
     55 
     56 
     57 #ifndef _ELF64
     58 /*
     59  * We supply this function for the msg module. Only one copy is needed.
     60  */
     61 const char *
     62 _str_msg(Msg mid)
     63 {
     64 	return (gettext(MSG_ORIG(mid)));
     65 }
     66 
     67 #endif
     68 
     69 
     70 
     71 /*
     72  * This function is supplied to elfedit through our elfedit_module_t
     73  * definition. It translates the opaque elfedit_i18nhdl_t handles
     74  * in our module interface into the actual strings for elfedit to
     75  * use.
     76  *
     77  * note:
     78  *	This module uses Msg codes for its i18n handle type.
     79  *	So the translation is simply to use MSG_INTL() to turn
     80  *	it into a string and return it.
     81  */
     82 static const char *
     83 mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
     84 {
     85 	Msg msg = (Msg)hdl;
     86 
     87 	return (MSG_INTL(msg));
     88 }
     89 
     90 
     91 
     92 /*
     93  * The sym_opt_t enum specifies a bit value for every optional
     94  * argument allowed by a command in this module.
     95  */
     96 typedef enum {
     97 	STR_OPT_F_ANY =		1,	/* -any: treat any sec. as strtab */
     98 	STR_OPT_F_END =		2,	/* -end: zero to end of strtab */
     99 	STR_OPT_F_NOTERM =	4,	/* -noterm: str:set won't term string */
    100 	STR_OPT_F_SHNAME =	8,	/* -shnam name: section spec. by name */
    101 	STR_OPT_F_SHNDX =	16,	/* -shndx ndx: strtab spec. by index */
    102 	STR_OPT_F_SHTYP =	32,	/* -shtyp type: section spec. by type */
    103 	STR_OPT_F_STRNDX =	64,	/* -strndx: String specified by index */
    104 } str_opt_t;
    105 
    106 
    107 /*
    108  * A variable of type ARGSTATE is used by each command to maintain
    109  * information about the string table section being used, and for any
    110  * auxiliary sections that are related to it.
    111  */
    112 typedef struct {
    113 	elfedit_obj_state_t	*obj_state;
    114 	str_opt_t		optmask;   	/* Mask of options used */
    115 	int			argc;		/* # of plain arguments */
    116 	const char		**argv;		/* Plain arguments */
    117 
    118 	struct {				/* String table */
    119 		elfedit_section_t	*sec;
    120 		Word			ndx;	/* Table offset if (argc > 0) */
    121 	} str;
    122 	struct {				/* Dynamic section */
    123 		elfedit_section_t	*sec;
    124 		Dyn			*data;
    125 		Word			n;
    126 		elfedit_dyn_elt_t	strpad;
    127 	} dyn;
    128 } ARGSTATE;
    129 
    130 
    131 
    132 /*
    133  * Given an ELF SHT_ section type constant, shndx_to_strtab() returns
    134  * one of the following
    135  */
    136 
    137 typedef enum {
    138 	SHTOSTR_NONE = 0,		/* Type can't lead to a  string table */
    139 	SHTOSTR_STRTAB = 1,		/* type is SHT_STRTAB */
    140 	SHTOSTR_LINK_STRTAB = 2,	/* sh_link for type yields strtab */
    141 	SHTOSTR_LINK_SYMTAB = 3,	/* sh_link for type yields symtab */
    142 	SHTOSTR_SHF_STRINGS = 4,	/* Not strtab, but SHF_STRINGS set */
    143 } SHTOSTR_T;
    144 
    145 static SHTOSTR_T
    146 shtype_to_strtab(Word sh_type, Word sh_flags)
    147 {
    148 	/*
    149 	 * A string table section always leads to itself. A
    150 	 * non-string table that has it's SHF_STRINGS section flag
    151 	 * set trumps anything else.
    152 	 */
    153 	if (sh_type == SHT_STRTAB)
    154 		return (SHTOSTR_STRTAB);
    155 	if (sh_flags & SHF_STRINGS)
    156 		return (SHTOSTR_SHF_STRINGS);
    157 
    158 	/*
    159 	 * Look at non-stringtable section types that can lead to
    160 	 * string tables via sh_link.
    161 	 */
    162 	switch (sh_type) {
    163 	/* These sections reference a string table via sh_link */
    164 	case SHT_DYNAMIC:
    165 	case SHT_SYMTAB:
    166 	case SHT_DYNSYM:
    167 	case SHT_SUNW_LDYNSYM:
    168 	case SHT_SUNW_verdef:
    169 	case SHT_SUNW_verneed:
    170 		return (SHTOSTR_LINK_STRTAB);
    171 
    172 	/*
    173 	 * These sections reference a symbol table via sh_link.
    174 	 * Symbol tables, in turn, reference a string table
    175 	 * via their sh_link.
    176 	 */
    177 	case SHT_HASH:
    178 	case SHT_REL:
    179 	case SHT_RELA:
    180 	case SHT_GROUP:
    181 	case SHT_SYMTAB_SHNDX:
    182 	case SHT_SUNW_move:
    183 	case SHT_SUNW_syminfo:
    184 	case SHT_SUNW_versym:
    185 	case SHT_SUNW_symsort:
    186 	case SHT_SUNW_tlssort:
    187 		return (SHTOSTR_LINK_SYMTAB);
    188 	}
    189 
    190 	/* Types that lead to string tables were caught above */
    191 	return (SHTOSTR_NONE);
    192 }
    193 
    194 /*
    195  * Given a section index, attempt to convert it into an index
    196  * to a string table section.
    197  */
    198 static Word
    199 shndx_to_strtab(elfedit_obj_state_t *obj_state, Word ndx)
    200 {
    201 	/*
    202 	 * Locate and validate the string table. In the case where
    203 	 * a non-string table section is given that references a string
    204 	 * table, we will use the referenced table.
    205 	 */
    206 	if (ndx < obj_state->os_shnum) {
    207 		Shdr *shdr = obj_state->os_secarr[ndx].sec_shdr;
    208 
    209 		switch (shtype_to_strtab(shdr->sh_type, shdr->sh_flags)) {
    210 
    211 		/* Sections that reference a string table via sh_link */
    212 		case SHTOSTR_LINK_STRTAB:
    213 			ndx = shdr->sh_link;
    214 			break;
    215 
    216 		/*
    217 		 * Sections that reference a symbol tabel via sh_link,
    218 		 * which in turn reference a string table via their sh_link.
    219 		 */
    220 		case SHTOSTR_LINK_SYMTAB:
    221 			ndx = shdr->sh_link;
    222 			if (ndx < obj_state->os_shnum)
    223 				ndx =
    224 				    obj_state->os_secarr[ndx].sec_shdr->sh_link;
    225 			break;
    226 		}
    227 	}
    228 
    229 	return (ndx);
    230 }
    231 
    232 
    233 
    234 /*
    235  * Standard argument processing for string table module
    236  *
    237  * entry
    238  *	obj_state, argc, argv - Standard command arguments
    239  *	optmask - Mask of allowed optional arguments.
    240  *	argstate - Address of ARGSTATE block to be initialized
    241  *
    242  * exit:
    243  *	On success, *argstate is initialized. On error,
    244  *	an error is issued and this routine does not return.
    245  */
    246 static void
    247 process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[],
    248     STR_CMD_T cmd, ARGSTATE *argstate, int *print_only)
    249 {
    250 	elfedit_getopt_state_t	getopt_state;
    251 	elfedit_getopt_ret_t	*getopt_ret;
    252 	Word			ndx;
    253 	int			argc_ok;
    254 
    255 	bzero(argstate, sizeof (*argstate));
    256 	argstate->obj_state = obj_state;
    257 
    258 	/*
    259 	 * By default, we use the section name string table pointed at
    260 	 * by the ELF header.
    261 	 */
    262 	ndx = obj_state->os_ehdr->e_shstrndx;
    263 
    264 	elfedit_getopt_init(&getopt_state, &argc, &argv);
    265 
    266 	/* Add each new option to the options mask */
    267 	while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
    268 		argstate->optmask |= getopt_ret->gor_idmask;
    269 
    270 		switch (getopt_ret->gor_idmask) {
    271 		case STR_OPT_F_SHNAME:		/* -shnam name */
    272 			ndx = elfedit_name_to_shndx(obj_state,
    273 			    getopt_ret->gor_value);
    274 			break;
    275 
    276 		case STR_OPT_F_SHNDX:		/* -shndx index */
    277 			ndx = elfedit_atoui(getopt_ret->gor_value, NULL);
    278 			break;
    279 
    280 		case STR_OPT_F_SHTYP:		/* -shtyp type */
    281 			ndx = elfedit_type_to_shndx(obj_state,
    282 			    elfedit_atoconst(getopt_ret->gor_value,
    283 			    ELFEDIT_CONST_SHT));
    284 			break;
    285 		}
    286 	}
    287 
    288 	/*
    289 	 * Usage error if there are the wrong number of plain arguments.
    290 	 */
    291 	switch (cmd) {
    292 	case STR_CMD_T_DUMP:
    293 		argc_ok = (argc == 0) || (argc == 1);
    294 		*print_only = 1;
    295 		break;
    296 	case STR_CMD_T_SET:
    297 		argc_ok = (argc == 1) || (argc == 2);
    298 		*print_only = (argc == 1);
    299 		break;
    300 	case STR_CMD_T_ADD:
    301 		argc_ok = (argc == 1);
    302 		*print_only = 0;
    303 		break;
    304 	case STR_CMD_T_ZERO:
    305 		/*
    306 		 * The second argument (count) and the -end option are
    307 		 * mutally exclusive.
    308 		 */
    309 		argc_ok = ((argc == 1) || (argc == 2)) &&
    310 		    !((argc == 2) && (argstate->optmask & STR_OPT_F_END));
    311 		*print_only = 0;
    312 		break;
    313 	default:
    314 		argc_ok = 0;	/* Unknown command? */
    315 		break;
    316 	}
    317 	if (!argc_ok)
    318 		elfedit_command_usage();
    319 
    320 	/* If there may be an arbitrary amount of output, use a pager */
    321 	if (argc == 0)
    322 		elfedit_pager_init();
    323 
    324 	/* Return the updated values of argc/argv */
    325 	argstate->argc = argc;
    326 	argstate->argv = argv;
    327 
    328 	if (argstate->optmask & STR_OPT_F_ANY) {
    329 		/* Take the arbitrary section */
    330 		argstate->str.sec = elfedit_sec_get(obj_state, ndx);
    331 
    332 	} else {
    333 		/*
    334 		 * Locate and validate the string table. In the case where
    335 		 * a non-string table section is given that references a string
    336 		 * table, we will use the referenced table.
    337 		 */
    338 		ndx = shndx_to_strtab(obj_state, ndx);
    339 
    340 		/*
    341 		 * If ndx is a string table, the following will issue the
    342 		 * proper debug messages. If it is out of range, or of any
    343 		 * other type, an error is issued and it doesn't return.
    344 		 */
    345 		argstate->str.sec = elfedit_sec_getstr(obj_state, ndx, 1);
    346 	}
    347 
    348 	/*
    349 	 * If there is a dynamic section, check its sh_link to the
    350 	 * string table index. If these match, then we have the
    351 	 * dynamic string table. In that case, fetch the dynamic
    352 	 * section and locate the DT_SUNW_STRPAD entry, causing
    353 	 * debug messages to be issued.
    354 	 */
    355 	argstate->dyn.sec = NULL;
    356 	elfedit_dyn_elt_init(&argstate->dyn.strpad);
    357 	if (obj_state->os_dynndx != SHN_UNDEF) {
    358 		elfedit_section_t *dynsec =
    359 		    &obj_state->os_secarr[obj_state->os_dynndx];
    360 
    361 		if ((dynsec->sec_shdr->sh_type == SHT_DYNAMIC) &&
    362 		    (argstate->str.sec->sec_shndx ==
    363 		    dynsec->sec_shdr->sh_link)) {
    364 			argstate->dyn.sec = elfedit_sec_getdyn(obj_state,
    365 			    &argstate->dyn.data, &argstate->dyn.n);
    366 			(void) elfedit_dynstr_getpad(obj_state, dynsec,
    367 			    &argstate->dyn.strpad);
    368 
    369 			/*
    370 			 * Does the pad value make sense?
    371 			 * Issue debug message and ignore it if not.
    372 			 */
    373 			if ((argstate->dyn.strpad.dn_seen != 0) &&
    374 			    (argstate->dyn.strpad.dn_dyn.d_un.d_val >
    375 			    argstate->str.sec->sec_data->d_size)) {
    376 				argstate->dyn.strpad.dn_seen = 0;
    377 				elfedit_msg(ELFEDIT_MSG_DEBUG,
    378 				    MSG_INTL(MSG_DEBUG_BADSTRPAD),
    379 				    EC_WORD(argstate->str.sec->sec_shndx),
    380 				    argstate->str.sec->sec_name,
    381 				    EC_XWORD(argstate->dyn.strpad.dn_dyn.
    382 				    d_un.d_val),
    383 				    EC_XWORD(argstate->str.sec->
    384 				    sec_data->d_size));
    385 
    386 			}
    387 		}
    388 	}
    389 
    390 	/* Locate the string table offset if argument is present */
    391 	if ((argc > 0) && (cmd != STR_CMD_T_ADD)) {
    392 		/*
    393 		 * If the -strndx option was specified, arg is an index
    394 		 * into the string table. Otherwise it is a string
    395 		 * to be looked up.
    396 		 */
    397 		if (argstate->optmask & STR_OPT_F_STRNDX) {
    398 			argstate->str.ndx = (elfedit_atoui_range(argv[0],
    399 			    MSG_ORIG(MSG_STR_STRING), 0,
    400 			    argstate->str.sec->sec_data->d_size - 1, NULL));
    401 		} else {
    402 			if (elfedit_sec_findstr(argstate->str.sec, 0, argv[0],
    403 			    &argstate->str.ndx) == 0)
    404 				elfedit_msg(ELFEDIT_MSG_ERR,
    405 				    MSG_INTL(MSG_ERR_STRNOTFND),
    406 				    EC_WORD(argstate->str.sec->sec_shndx),
    407 				    argstate->str.sec->sec_name, argv[0]);
    408 		}
    409 	} else {
    410 		argstate->str.ndx = 0;
    411 	}
    412 }
    413 
    414 
    415 
    416 /*
    417  * Print string table values, taking output style into account.
    418  *
    419  * entry:
    420  *	autoprint - If True, output is only produced if the elfedit
    421  *		autoprint flag is set. If False, output is always produced.
    422  *	argstate - State block for current symbol table.
    423  */
    424 static void
    425 print_strtab(int autoprint, ARGSTATE *argstate)
    426 {
    427 	char			index[(MAXNDXSIZE * 2) + 4];
    428 	elfedit_outstyle_t	outstyle;
    429 	const char		*str, *limit, *tbl_limit;
    430 	Word			ndx;
    431 
    432 
    433 	if (autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0))
    434 		return;
    435 
    436 	outstyle = elfedit_outstyle();
    437 	if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
    438 		elfedit_printf(MSG_INTL(MSG_FMT_STRTAB),
    439 		    argstate->str.sec->sec_name);
    440 		if (argstate->dyn.strpad.dn_seen)
    441 			elfedit_printf(MSG_INTL(MSG_FMT_DYNSTRPAD),
    442 			    EC_WORD(argstate->str.sec->sec_data->d_size -
    443 			    argstate->dyn.strpad.dn_dyn.d_un.d_val),
    444 			    EC_WORD(argstate->str.sec->sec_data->d_size - 1),
    445 			    EC_WORD(argstate->dyn.strpad.dn_dyn.d_un.d_val));
    446 		elfedit_printf(MSG_INTL(MSG_FMT_DUMPTITLE));
    447 	}
    448 
    449 	str = argstate->str.sec->sec_data->d_buf;
    450 	tbl_limit = str + argstate->str.sec->sec_data->d_size;
    451 	ndx = argstate->str.ndx;
    452 	if (argstate->argc > 0) {
    453 		str += ndx;
    454 		/*
    455 		 * If first byte is NULL and this is the default output style,
    456 		 * then we want to display the range of NULL bytes, and we
    457 		 * push limit out to the last one in the sequence. Otherwise,
    458 		 * just display the string.
    459 		 */
    460 		if ((*str == '\0') && (outstyle == ELFEDIT_OUTSTYLE_DEFAULT)) {
    461 			limit = str;
    462 			while (((limit + 1) < tbl_limit) &&
    463 			    (*(limit + 1) == '\0'))
    464 				limit++;
    465 		} else {
    466 			limit = str + strlen(str) + 1;
    467 		}
    468 	} else {
    469 		/* Display the entire string table  */
    470 		limit = tbl_limit;
    471 	}
    472 
    473 
    474 	while (str < limit) {
    475 		Word	skip = strlen(str) + 1;
    476 		Word	start_ndx;
    477 
    478 		if (outstyle != ELFEDIT_OUTSTYLE_DEFAULT) {
    479 			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), str);
    480 			str += skip;
    481 			ndx += skip;
    482 			continue;
    483 		}
    484 
    485 		start_ndx = ndx;
    486 		if (*str == '\0')
    487 			while (((str + 1) < limit) && (*(str + 1) == '\0')) {
    488 				ndx++;
    489 				str++;
    490 			}
    491 
    492 		if (start_ndx != ndx) {
    493 			(void) snprintf(index, sizeof (index),
    494 			    MSG_ORIG(MSG_FMT_INDEXRANGE),
    495 			    EC_XWORD(start_ndx), EC_XWORD(ndx));
    496 		} else {
    497 			(void) snprintf(index, sizeof (index),
    498 			    MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(ndx));
    499 		}
    500 		elfedit_printf(MSG_ORIG(MSG_FMT_DUMPENTRY), index);
    501 		elfedit_write(MSG_ORIG(MSG_STR_DQUOTE), MSG_STR_DQUOTE_SIZE);
    502 		if (start_ndx == ndx)
    503 			elfedit_str_to_c_literal(str, elfedit_write);
    504 		elfedit_write(MSG_ORIG(MSG_STR_DQUOTENL),
    505 		    MSG_STR_DQUOTENL_SIZE);
    506 		str += skip;
    507 		ndx += skip;
    508 	}
    509 }
    510 
    511 
    512 /*
    513  * Command body for str:set, handling the case where the 3rd
    514  * argument (new-str) is present.
    515  */
    516 static elfedit_cmdret_t
    517 cmd_body_set(ARGSTATE *argstate)
    518 {
    519 	elfedit_section_t	*strsec = argstate->str.sec;
    520 	const char		*newstr = argstate->argv[1];
    521 	Word	ndx = argstate->str.ndx;
    522 	char	*oldstr;
    523 	int	i, len, ncp;
    524 
    525 	len = strlen(newstr);
    526 	ncp = len;
    527 	if (!(argstate->optmask & STR_OPT_F_NOTERM))
    528 		ncp++;
    529 
    530 	/* NULL string with no termination? Nothing to do */
    531 	if (ncp == 0)
    532 		return (ELFEDIT_CMDRET_NONE);
    533 
    534 	/* Does it fit? */
    535 	if ((ndx + ncp) > strsec->sec_data->d_size)
    536 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFIT),
    537 		    EC_WORD(strsec->sec_shndx), strsec->sec_name,
    538 		    EC_WORD(ndx), newstr);
    539 
    540 	/* Does it clobber the final NULL termination? */
    541 	if (((ndx + ncp) == strsec->sec_data->d_size) &&
    542 	    (argstate->optmask & STR_OPT_F_NOTERM))
    543 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FINALNULL),
    544 		    EC_WORD(strsec->sec_shndx), strsec->sec_name,
    545 		    EC_WORD(ndx), newstr);
    546 
    547 	/*
    548 	 * strtab[0] is always supposed to contain a NULL byte. You're not
    549 	 * supposed to mess with it. We will carry out this operation,
    550 	 * but with a debug message indicating that it is unorthodox.
    551 	 */
    552 	if ((ndx == 0) && (*newstr != '\0'))
    553 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CHGSTR0),
    554 		    EC_WORD(strsec->sec_shndx), strsec->sec_name,
    555 		    EC_WORD(ndx), newstr);
    556 
    557 	/* Does it alter the existing value? */
    558 	oldstr = ndx + (char *)strsec->sec_data->d_buf;
    559 	for (i = 0; i < ncp; i++)
    560 		if (newstr[i] != oldstr[i])
    561 			break;
    562 	if (i == ncp) {		/* No change */
    563 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK),
    564 		    strsec->sec_shndx, strsec->sec_name, ndx, newstr);
    565 		return (ELFEDIT_CMDRET_NONE);
    566 	}
    567 
    568 	/*
    569 	 * If the new string is longer than the old one, then it will
    570 	 * clobber the start of the following string. The resulting
    571 	 * string table is perfectly legal, but issue a debug message
    572 	 * letting the user know.
    573 	 */
    574 	i = strlen(oldstr);
    575 	if (len > i)
    576 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LONGSTR),
    577 		    EC_WORD(strsec->sec_shndx), strsec->sec_name,
    578 		    EC_WORD(ndx), len, i);
    579 
    580 	/*
    581 	 * If we have strayed into the reserved part of the dynstr, then
    582 	 * update DT_SUNW_STRPAD.
    583 	 */
    584 	if (argstate->dyn.strpad.dn_seen) {
    585 		elfedit_dyn_elt_t	*strpad = &argstate->dyn.strpad;
    586 		Word	new_pad_ndx = ndx + len + 1;
    587 		Word	pad_ndx = argstate->str.sec->sec_data->d_size -
    588 		    strpad->dn_dyn.d_un.d_val;
    589 
    590 		if (new_pad_ndx > pad_ndx) {
    591 			elfedit_msg(ELFEDIT_MSG_DEBUG,
    592 			    MSG_INTL(MSG_DEBUG_ADDDYNSTR),
    593 			    EC_WORD(strsec->sec_shndx), strsec->sec_name,
    594 			    EC_WORD(ndx), EC_WORD(new_pad_ndx - pad_ndx),
    595 			    EC_WORD(strpad->dn_dyn.d_un.d_val),
    596 			    newstr);
    597 
    598 			strpad->dn_dyn.d_un.d_val =
    599 			    argstate->dyn.data[strpad->dn_ndx].d_un.d_val =
    600 			    (argstate->str.sec->sec_data->d_size - new_pad_ndx);
    601 			elfedit_modified_data(argstate->dyn.sec);
    602 		}
    603 	}
    604 
    605 
    606 
    607 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG),
    608 	    strsec->sec_shndx, strsec->sec_name, ndx, len, oldstr, newstr);
    609 	bcopy(newstr, oldstr, ncp);
    610 
    611 	return (ELFEDIT_CMDRET_MOD);
    612 }
    613 
    614 
    615 /*
    616  * Command body for str:zero
    617  */
    618 static elfedit_cmdret_t
    619 cmd_body_zero(ARGSTATE *argstate)
    620 {
    621 	elfedit_section_t	*strsec = argstate->str.sec;
    622 	Word	count;
    623 	Word	ndx = argstate->str.ndx;
    624 	char	*oldstr = ndx + (char *)strsec->sec_data->d_buf;
    625 	Word	i;
    626 
    627 	/* How many bytes to zero? */
    628 	if (argstate->optmask & STR_OPT_F_END)
    629 		count = strsec->sec_data->d_size - argstate->str.ndx;
    630 	else if (argstate->argc == 2)
    631 		count = elfedit_atoui_range(argstate->argv[1],
    632 		    MSG_ORIG(MSG_STR_COUNT), 0,
    633 		    argstate->str.sec->sec_data->d_size - argstate->str.ndx,
    634 		    NULL);
    635 	else
    636 		count = strlen(oldstr);
    637 
    638 	/* Does it alter the existing value? */
    639 	for (i = 0; i < count; i++)
    640 		if (oldstr[i] != '\0')
    641 			break;
    642 	if (i == count) {		/* No change */
    643 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_Z_OK),
    644 		    strsec->sec_shndx, strsec->sec_name, ndx);
    645 		return (ELFEDIT_CMDRET_NONE);
    646 	}
    647 
    648 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_Z_CHG),
    649 	    strsec->sec_shndx, strsec->sec_name, ndx, count);
    650 	bzero(oldstr, count);
    651 
    652 	return (ELFEDIT_CMDRET_MOD);
    653 }
    654 
    655 
    656 /*
    657  * Common body for the str: module commands.
    658  *
    659  * entry:
    660  *	cmd - One of the STR_CMD_T_* constants listed above, specifying
    661  *		which command to implement.
    662  *	obj_state, argc, argv - Standard command arguments
    663  */
    664 static elfedit_cmdret_t
    665 cmd_body(STR_CMD_T cmd, elfedit_obj_state_t *obj_state,
    666     int argc, const char *argv[])
    667 {
    668 	ARGSTATE		argstate;
    669 	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
    670 	int			print_only;
    671 
    672 	process_args(obj_state, argc, argv, cmd, &argstate, &print_only);
    673 
    674 	/*
    675 	 * If this call call does not change data, display the current
    676 	 * value(s) and return.
    677 	 */
    678 	if (print_only) {
    679 		print_strtab(0, &argstate);
    680 		return (ELFEDIT_CMDRET_NONE);
    681 	}
    682 
    683 	switch (cmd) {
    684 	/* NOTE: STR_CMD_T_DUMP can't get here --- it's always print_only */
    685 
    686 	case STR_CMD_T_SET:
    687 		ret = cmd_body_set(&argstate);
    688 		break;
    689 
    690 	case STR_CMD_T_ADD:
    691 		argstate.str.ndx = elfedit_strtab_insert(obj_state,
    692 		    argstate.str.sec, argstate.dyn.sec, argstate.argv[0]);
    693 		break;
    694 
    695 	case STR_CMD_T_ZERO:
    696 		ret = cmd_body_zero(&argstate);
    697 		break;
    698 	}
    699 
    700 	/*
    701 	 * If we modified the strtab section, tell libelf.
    702 	 */
    703 	if (ret == ELFEDIT_CMDRET_MOD)
    704 		elfedit_modified_data(argstate.str.sec);
    705 
    706 	/* Do autoprint */
    707 	print_strtab(1, &argstate);
    708 
    709 	return (ret);
    710 }
    711 
    712 
    713 
    714 
    715 /*
    716  * Command completion functions for the various commands
    717  */
    718 
    719 static void
    720 add_shtyp_match(Word sh_type, void *cpldata)
    721 {
    722 	char		buf[128];
    723 	const char	*s;
    724 	char		*s2;
    725 
    726 	s = elfedit_atoconst_value_to_str(ELFEDIT_CONST_SHT, sh_type, 0);
    727 	elfedit_cpl_match(cpldata, s, 1);
    728 
    729 	/*
    730 	 * To get the informal tag names that are lowercase
    731 	 * and lack the leading SHT_, we copy the string we
    732 	 * have into a buffer and process it.
    733 	 */
    734 	if (strlen(s) < 4)
    735 		return;
    736 	(void) strlcpy(buf, s + 4, sizeof (buf));
    737 	for (s2 = buf; *s2 != '\0'; s2++)
    738 		if (isupper(*s2))
    739 			*s2 = tolower(*s2);
    740 	elfedit_cpl_match(cpldata, buf, 1);
    741 }
    742 
    743 /*
    744  * Handle filling in the values for -shnam, -shndx, and -shtyp options.
    745  */
    746 /*ARGSUSED*/
    747 static void
    748 cpl_sh_opt(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    749     const char *argv[], int num_opt)
    750 {
    751 	enum { NAME, INDEX, TYPE }	op;
    752 	elfedit_section_t		*sec;
    753 	Word 	ndx;
    754 
    755 	if ((argc != num_opt) || (argc < 2))
    756 		return;
    757 
    758 	if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNAM)) == 0) {
    759 		op = NAME;
    760 	} else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0) {
    761 		op = INDEX;
    762 
    763 	} else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 0) {
    764 		op = TYPE;
    765 
    766 		if (obj_state == NULL) {	 /* No object available */
    767 			elfedit_atoui_sym_t *atoui_sym;
    768 
    769 			atoui_sym = elfedit_const_to_atoui(ELFEDIT_CONST_SHT);
    770 			for (; atoui_sym->sym_name != NULL; atoui_sym++)
    771 				if (shtype_to_strtab(atoui_sym->sym_value, 0) !=
    772 				    SHTOSTR_NONE)
    773 					elfedit_cpl_match(cpldata,
    774 					    atoui_sym->sym_name, 1);
    775 		}
    776 	} else {
    777 		return;
    778 	}
    779 
    780 	if (obj_state == NULL)	 /* No object available */
    781 		return;
    782 
    783 	/*
    784 	 * Loop over the section headers and supply command completion
    785 	 * for the items in the file that can yield a string table.
    786 	 */
    787 	sec = obj_state->os_secarr;
    788 	for (ndx = 0; ndx < obj_state->os_shnum; ndx++, sec++) {
    789 		Shdr		*shdr = sec->sec_shdr;
    790 		SHTOSTR_T	shtostr_type;
    791 
    792 		shtostr_type = shtype_to_strtab(shdr->sh_type, shdr->sh_flags);
    793 		if (shtostr_type == SHTOSTR_NONE)
    794 			continue;
    795 
    796 		switch (op) {
    797 		case NAME:
    798 			elfedit_cpl_match(cpldata, sec->sec_name, 0);
    799 			break;
    800 		case INDEX:
    801 			elfedit_cpl_ndx(cpldata, sec->sec_shndx);
    802 			break;
    803 		case TYPE:
    804 			if (shtostr_type != SHTOSTR_SHF_STRINGS)
    805 				add_shtyp_match(shdr->sh_type, cpldata);
    806 			break;
    807 		}
    808 	}
    809 }
    810 
    811 
    812 /*
    813  * Most of the commands accept an -shXXX option for the string table
    814  * and a string first argument. This routine examines which argument
    815  * is being processed, and supplies completion for these items.
    816  */
    817 static void
    818 cpl_sec_str(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    819     const char *argv[], int num_opt)
    820 {
    821 	const char		*str, *limit;
    822 	elfedit_section_t	*sec;
    823 	Word			strtab_ndx;
    824 	Word			ndx;
    825 
    826 	/* Handle -shXXX options */
    827 	cpl_sh_opt(obj_state, cpldata, argc, argv, num_opt);
    828 
    829 	/* Without object state, there's no data to work from */
    830 	if (obj_state == NULL)
    831 		return;
    832 
    833 	/* If not first plain arg, return */
    834 	if (argc != (num_opt + 1))
    835 		return;
    836 
    837 	/*
    838 	 * Look at the options, looking for two things:
    839 	 *	1) A -shXXX option specifying a section. If so, turn that
    840 	 *		into a section index if possible.
    841 	 *	2) Was -strndx used? If so, we are looking at an integer
    842 	 *		value and have nothing to complete.
    843 	 */
    844 	strtab_ndx = obj_state->os_ehdr->e_shstrndx;
    845 	for (ndx = 0; ndx < num_opt; ndx++) {
    846 		if (strcmp(argv[ndx], MSG_ORIG(MSG_STR_MINUS_STRNDX)) == 0)
    847 			return;
    848 
    849 		if ((ndx+1) < num_opt) {
    850 			if (strcmp(argv[ndx],
    851 			    MSG_ORIG(MSG_STR_MINUS_SHNAM)) == 0) {
    852 				Word		i;
    853 
    854 				for (i = 1; i < obj_state->os_shnum; i++)
    855 					if (strcmp(obj_state->os_secarr[i].
    856 					    sec_name, argv[ndx+1]) == 0) {
    857 						strtab_ndx = i;
    858 						break;
    859 					}
    860 			} else if (strcmp(argv[ndx],
    861 			    MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0) {
    862 				elfedit_atoui_t val;
    863 
    864 				if (elfedit_atoui2(argv[ndx+1], NULL,
    865 				    &val) != 0)
    866 					strtab_ndx = val;
    867 			} else if (strcmp(argv[ndx],
    868 			    MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 0) {
    869 				elfedit_atoui_t	sh_type;
    870 				Word		i;
    871 
    872 				if (elfedit_atoconst2(argv[ndx+1],
    873 				    ELFEDIT_CONST_SHT, &sh_type) == 0)
    874 					continue;
    875 				for (i = 1; i < obj_state->os_shnum; i++)
    876 					if (obj_state->os_secarr[i].sec_shdr->
    877 					    sh_type == sh_type) {
    878 						strtab_ndx = i;
    879 						break;
    880 					}
    881 			}
    882 		}
    883 	}
    884 
    885 	/*
    886 	 * Locate and validate the string table. In the case where
    887 	 * a non-string table section is given that references a string
    888 	 * table, we will use the referenced table.
    889 	 */
    890 	strtab_ndx = shndx_to_strtab(obj_state, strtab_ndx);
    891 	if ((strtab_ndx >= obj_state->os_shnum) ||
    892 	    (obj_state->os_secarr[strtab_ndx].sec_shdr->sh_type != SHT_STRTAB))
    893 		return;
    894 	sec = &obj_state->os_secarr[strtab_ndx];
    895 
    896 	str = sec->sec_data->d_buf;
    897 	limit = str + sec->sec_data->d_size;
    898 	while (str < limit) {
    899 		if (*str != '\0')
    900 			elfedit_cpl_match(cpldata, str, 0);
    901 		str += strlen(str) + 1;
    902 	}
    903 }
    904 
    905 
    906 
    907 /*
    908  * Implementation functions for the commands
    909  */
    910 static elfedit_cmdret_t
    911 cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
    912 {
    913 	return (cmd_body(STR_CMD_T_DUMP, obj_state, argc, argv));
    914 }
    915 
    916 static elfedit_cmdret_t
    917 cmd_set(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
    918 {
    919 	return (cmd_body(STR_CMD_T_SET, obj_state, argc, argv));
    920 }
    921 
    922 static elfedit_cmdret_t
    923 cmd_add(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
    924 {
    925 	return (cmd_body(STR_CMD_T_ADD, obj_state, argc, argv));
    926 }
    927 
    928 static elfedit_cmdret_t
    929 cmd_zero(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
    930 {
    931 	return (cmd_body(STR_CMD_T_ZERO, obj_state, argc, argv));
    932 }
    933 
    934 
    935 
    936 /*ARGSUSED*/
    937 elfedit_module_t *
    938 elfedit_init(elfedit_module_version_t version)
    939 {
    940 	/* str:dump */
    941 	static const char *name_dump[] = {
    942 	    MSG_ORIG(MSG_CMD_DUMP),
    943 	    MSG_ORIG(MSG_STR_EMPTY),	/* "" makes this the default command */
    944 	    NULL
    945 	};
    946 	static elfedit_cmd_optarg_t opt_dump[] = {
    947 		{ MSG_ORIG(MSG_STR_MINUS_ANY),
    948 		    /* MSG_INTL(MSG_OPTDESC_ANY) */
    949 		    ELFEDIT_I18NHDL(MSG_OPTDESC_ANY), 0,
    950 		    STR_OPT_F_ANY, 0 },
    951 		{ ELFEDIT_STDOA_OPT_O, NULL,
    952 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
    953 		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
    954 		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
    955 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
    956 		    STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
    957 		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
    958 		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
    959 		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
    960 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
    961 		    STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
    962 		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
    963 		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
    964 		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
    965 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
    966 		    STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
    967 		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
    968 		{ MSG_ORIG(MSG_STR_MINUS_STRNDX),
    969 		    /* MSG_INTL(MSG_OPTDESC_STRNDX) */
    970 		    ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
    971 		    STR_OPT_F_STRNDX, 0 },
    972 		{ NULL }
    973 	};
    974 	static elfedit_cmd_optarg_t arg_dump[] = {
    975 		{ MSG_ORIG(MSG_STR_STRING),
    976 		    /* MSG_INTL(MSG_A1_STRING) */
    977 		    ELFEDIT_I18NHDL(MSG_A1_STRING),
    978 		    ELFEDIT_CMDOA_F_OPT },
    979 		{ NULL }
    980 	};
    981 
    982 	/* str:set */
    983 	static const char *name_set[] = {
    984 	    MSG_ORIG(MSG_CMD_SET), NULL };
    985 	static elfedit_cmd_optarg_t opt_set[] = {
    986 		{ MSG_ORIG(MSG_STR_MINUS_ANY),
    987 		    /* MSG_INTL(MSG_OPTDESC_ANY) */
    988 		    ELFEDIT_I18NHDL(MSG_OPTDESC_ANY), 0,
    989 		    STR_OPT_F_ANY, 0 },
    990 		{ ELFEDIT_STDOA_OPT_O, NULL,
    991 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
    992 		{ MSG_ORIG(MSG_STR_MINUS_NOTERM),
    993 		    /* MSG_INTL(MSG_OPTDESC_NOTERM) */
    994 		    ELFEDIT_I18NHDL(MSG_OPTDESC_NOTERM), 0,
    995 		    STR_OPT_F_NOTERM, 0 },
    996 		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
    997 		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
    998 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
    999 		    STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
   1000 		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
   1001 		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
   1002 		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
   1003 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
   1004 		    STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
   1005 		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
   1006 		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
   1007 		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
   1008 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
   1009 		    STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
   1010 		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
   1011 		{ MSG_ORIG(MSG_STR_MINUS_STRNDX),
   1012 		    /* MSG_INTL(MSG_OPTDESC_STRNDX) */
   1013 		    ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
   1014 		    STR_OPT_F_STRNDX, 0 },
   1015 		{ NULL }
   1016 	};
   1017 	static elfedit_cmd_optarg_t arg_set[] = {
   1018 		{ MSG_ORIG(MSG_STR_STRING),
   1019 		    /* MSG_INTL(MSG_A1_STRING) */
   1020 		    ELFEDIT_I18NHDL(MSG_A1_STRING),
   1021 		    0 },
   1022 		{ MSG_ORIG(MSG_STR_NEWSTRING),
   1023 		    /* MSG_INTL(MSG_A2_NEWSTRING) */
   1024 		    ELFEDIT_I18NHDL(MSG_A2_NEWSTRING),
   1025 		    ELFEDIT_CMDOA_F_OPT },
   1026 		{ NULL }
   1027 	};
   1028 
   1029 	/* str:add */
   1030 	static const char *name_add[] = {
   1031 	    MSG_ORIG(MSG_CMD_ADD), NULL };
   1032 	static elfedit_cmd_optarg_t opt_add[] = {
   1033 		{ ELFEDIT_STDOA_OPT_O, NULL,
   1034 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
   1035 		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
   1036 		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
   1037 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
   1038 		    STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
   1039 		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
   1040 		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
   1041 		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
   1042 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
   1043 		    STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
   1044 		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
   1045 		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
   1046 		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
   1047 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
   1048 		    STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
   1049 		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
   1050 		{ NULL }
   1051 	};
   1052 	static elfedit_cmd_optarg_t arg_add[] = {
   1053 		{ MSG_ORIG(MSG_STR_NEWSTRING),
   1054 		    /* MSG_INTL(MSG_A1_NEWSTRING) */
   1055 		    ELFEDIT_I18NHDL(MSG_A1_NEWSTRING),
   1056 		    0 },
   1057 		{ NULL }
   1058 	};
   1059 
   1060 	/* str:zero */
   1061 	static const char *name_zero[] = {
   1062 	    MSG_ORIG(MSG_CMD_ZERO), NULL };
   1063 	static elfedit_cmd_optarg_t opt_zero[] = {
   1064 		{ MSG_ORIG(MSG_STR_MINUS_ANY),
   1065 		    /* MSG_INTL(MSG_OPTDESC_ANY) */
   1066 		    ELFEDIT_I18NHDL(MSG_OPTDESC_ANY), 0,
   1067 		    STR_OPT_F_ANY, 0 },
   1068 		{ ELFEDIT_STDOA_OPT_O, NULL,
   1069 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
   1070 		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
   1071 		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
   1072 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
   1073 		    STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
   1074 		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
   1075 		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
   1076 		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
   1077 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
   1078 		    STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
   1079 		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
   1080 		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
   1081 		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
   1082 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
   1083 		    STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
   1084 		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
   1085 		{ MSG_ORIG(MSG_STR_MINUS_STRNDX),
   1086 		    /* MSG_INTL(MSG_OPTDESC_STRNDX) */
   1087 		    ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
   1088 		    STR_OPT_F_STRNDX, 0 },
   1089 		{ MSG_ORIG(MSG_STR_MINUS_END),
   1090 		    /* MSG_INTL(MSG_OPTDESC_END) */
   1091 		    ELFEDIT_I18NHDL(MSG_OPTDESC_END), 0,
   1092 		    STR_OPT_F_END, 0 },
   1093 		{ NULL }
   1094 	};
   1095 	static elfedit_cmd_optarg_t arg_zero[] = {
   1096 		{ MSG_ORIG(MSG_STR_STRING),
   1097 		    /* MSG_INTL(MSG_A1_STRING) */
   1098 		    ELFEDIT_I18NHDL(MSG_A1_STRING),
   1099 		    0 },
   1100 		{ MSG_ORIG(MSG_STR_COUNT),
   1101 		    /* MSG_INTL(MSG_A2_COUNT) */
   1102 		    ELFEDIT_I18NHDL(MSG_A2_COUNT),
   1103 		    ELFEDIT_CMDOA_F_OPT },
   1104 		{ NULL }
   1105 	};
   1106 
   1107 
   1108 	static elfedit_cmd_t cmds[] = {
   1109 		/* str:dump */
   1110 		{ cmd_dump, cpl_sec_str, name_dump,
   1111 		    /* MSG_INTL(MSG_DESC_DUMP) */
   1112 		    ELFEDIT_I18NHDL(MSG_DESC_DUMP),
   1113 		    /* MSG_INTL(MSG_HELP_DUMP) */
   1114 		    ELFEDIT_I18NHDL(MSG_HELP_DUMP),
   1115 		    opt_dump, arg_dump },
   1116 
   1117 		/* str:set */
   1118 		{ cmd_set, cpl_sec_str, name_set,
   1119 		    /* MSG_INTL(MSG_DESC_SET) */
   1120 		    ELFEDIT_I18NHDL(MSG_DESC_SET),
   1121 		    /* MSG_INTL(MSG_HELP_SET) */
   1122 		    ELFEDIT_I18NHDL(MSG_HELP_SET),
   1123 		    opt_set, arg_set },
   1124 
   1125 		/* str:add */
   1126 		{ cmd_add, cpl_sh_opt, name_add,
   1127 		    /* MSG_INTL(MSG_DESC_ADD) */
   1128 		    ELFEDIT_I18NHDL(MSG_DESC_ADD),
   1129 		    /* MSG_INTL(MSG_HELP_ADD) */
   1130 		    ELFEDIT_I18NHDL(MSG_HELP_ADD),
   1131 		    opt_add, arg_add },
   1132 
   1133 		/* str:zero */
   1134 		{ cmd_zero, cpl_sec_str, name_zero,
   1135 		    /* MSG_INTL(MSG_DESC_ZERO) */
   1136 		    ELFEDIT_I18NHDL(MSG_DESC_ZERO),
   1137 		    /* MSG_INTL(MSG_HELP_ZERO) */
   1138 		    ELFEDIT_I18NHDL(MSG_HELP_ZERO),
   1139 		    opt_zero, arg_zero },
   1140 
   1141 		{ NULL }
   1142 	};
   1143 
   1144 	static elfedit_module_t module = {
   1145 	    ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_NAME),
   1146 	    /* MSG_INTL(MSG_MOD_DESC) */
   1147 	    ELFEDIT_I18NHDL(MSG_MOD_DESC),
   1148 	    cmds, mod_i18nhdl_to_str };
   1149 
   1150 	return (&module);
   1151 }
   1152