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