xref: /titanic_50/usr/src/cmd/sgs/elfedit/modules/common/str.c (revision 09b1eac246a4e627fcbd1ce5bf8005746cbe45ea)
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