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 2007 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	<machdep.h>
32 #include	<elfedit.h>
33 #include	<strings.h>
34 #include	<debug.h>
35 #include	<conv.h>
36 #include	<str_msg.h>
37 
38 
39 
40 
41 #define	MAXNDXSIZE	10
42 
43 
44 
45 /*
46  * This module uses shared code for several of the commands.
47  * It is sometimes necessary to know which specific command
48  * is active.
49  */
50 typedef enum {
51 	STR_CMD_T_DUMP =	0,	/* str:dump */
52 	STR_CMD_T_SET =		1,	/* str:set */
53 	STR_CMD_T_ADD =		2,	/* str:add */
54 	STR_CMD_T_ZERO =	3,	/* str:zero */
55 } STR_CMD_T;
56 
57 
58 
59 #ifndef _ELF64
60 /*
61  * We supply this function for the msg module. Only one copy is needed.
62  */
63 const char *
64 _str_msg(Msg mid)
65 {
66 	return (gettext(MSG_ORIG(mid)));
67 }
68 
69 #endif
70 
71 
72 
73 /*
74  * This function is supplied to elfedit through our elfedit_module_t
75  * definition. It translates the opaque elfedit_i18nhdl_t handles
76  * in our module interface into the actual strings for elfedit to
77  * use.
78  *
79  * note:
80  *	This module uses Msg codes for its i18n handle type.
81  *	So the translation is simply to use MSG_INTL() to turn
82  *	it into a string and return it.
83  */
84 static const char *
85 mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
86 {
87 	Msg msg = (Msg)hdl;
88 
89 	return (MSG_INTL(msg));
90 }
91 
92 
93 
94 /*
95  * The sym_opt_t enum specifies a bit value for every optional
96  * argument allowed by a command in this module.
97  */
98 typedef enum {
99 	STR_OPT_F_END =		1,	/* -end: zero to end of strtab */
100 	STR_OPT_F_NOTERM =	2,	/* -noterm: str:set won't term string */
101 	STR_OPT_F_SHNAME =	4,	/* -shnam name: section spec. by name */
102 	STR_OPT_F_SHNDX =	8,	/* -shndx ndx: strtab spec. by index */
103 	STR_OPT_F_SHTYP =	16,	/* -shtyp type: section spec. by type */
104 	STR_OPT_F_STRNDX =	32,	/* -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, shdr_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 	/*
317 	 * Locate and validate the string table. In the case where
318 	 * a non-string table section is given that references a string
319 	 * table, we will use the referenced table.
320 	 */
321 	ndx = shndx_to_strtab(obj_state, ndx);
322 
323 	/*
324 	 * If ndx is a string table, the following will issue the
325 	 * proper debug messages. If it is out of range, or of any
326 	 * other type, an error is issued and it doesn't return.
327 	 */
328 	argstate->str.sec = elfedit_sec_getstr(obj_state, ndx);
329 
330 	/*
331 	 * If there is a dynamic section, check its sh_link to the
332 	 * string table index. If these match, then we have the
333 	 * dynamic string table. In that case, fetch the dynamic
334 	 * section and locate the DT_SUNW_STRPAD entry, causing
335 	 * debug messages to be issued.
336 	 */
337 	argstate->dyn.sec = NULL;
338 	elfedit_dyn_elt_init(&argstate->dyn.strpad);
339 	if (obj_state->os_dynndx != SHN_UNDEF) {
340 		elfedit_section_t *dynsec =
341 		    &obj_state->os_secarr[obj_state->os_dynndx];
342 
343 		if ((dynsec->sec_shdr->sh_type == SHT_DYNAMIC) &&
344 		    (argstate->str.sec->sec_shndx ==
345 		    dynsec->sec_shdr->sh_link)) {
346 			argstate->dyn.sec = elfedit_sec_getdyn(obj_state,
347 			    &argstate->dyn.data, &argstate->dyn.n);
348 			(void) elfedit_dynstr_getpad(dynsec,
349 			    &argstate->dyn.strpad);
350 
351 			/*
352 			 * Does the pad value make sense?
353 			 * Issue debug message and ignore it if not.
354 			 */
355 			if ((argstate->dyn.strpad.dn_seen != 0) &&
356 			    (argstate->dyn.strpad.dn_dyn.d_un.d_val >
357 			    argstate->str.sec->sec_data->d_size)) {
358 				argstate->dyn.strpad.dn_seen = 0;
359 				elfedit_msg(ELFEDIT_MSG_DEBUG,
360 				    MSG_INTL(MSG_DEBUG_BADSTRPAD),
361 				    EC_WORD(argstate->str.sec->sec_shndx),
362 				    argstate->str.sec->sec_name,
363 				    EC_XWORD(argstate->dyn.strpad.dn_dyn.
364 				    d_un.d_val),
365 				    EC_XWORD(argstate->str.sec->
366 				    sec_data->d_size));
367 
368 			}
369 		}
370 	}
371 
372 	/* Locate the string table offset if argument is present */
373 	if ((argc > 0) && (cmd != STR_CMD_T_ADD)) {
374 		/*
375 		 * If the -strndx option was specified, arg is an index
376 		 * into the string table. Otherwise it is a string
377 		 * to be looked up.
378 		 */
379 		if (argstate->optmask & STR_OPT_F_STRNDX) {
380 			argstate->str.ndx = (elfedit_atoui_range(argv[0],
381 			    MSG_ORIG(MSG_STR_STRING), 0,
382 			    argstate->str.sec->sec_data->d_size - 1, NULL));
383 		} else {
384 			if (elfedit_sec_findstr(argstate->str.sec, 0, argv[0],
385 			    &argstate->str.ndx) == 0)
386 				elfedit_msg(ELFEDIT_MSG_ERR,
387 				    MSG_INTL(MSG_ERR_STRNOTFND),
388 				    EC_WORD(argstate->str.sec->sec_shndx),
389 				    argstate->str.sec->sec_name, argv[0]);
390 		}
391 	} else {
392 		argstate->str.ndx = 0;
393 	}
394 }
395 
396 
397 
398 /*
399  * Print string table values, taking output style into account.
400  *
401  * entry:
402  *	autoprint - If True, output is only produced if the elfedit
403  *		autoprint flag is set. If False, output is always produced.
404  *	argstate - State block for current symbol table.
405  */
406 static void
407 print_strtab(int autoprint, ARGSTATE *argstate)
408 {
409 	char			index[(MAXNDXSIZE * 2) + 4];
410 	elfedit_outstyle_t	outstyle;
411 	const char		*str, *limit, *tbl_limit;
412 	Word			ndx;
413 
414 
415 	if (autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0))
416 		return;
417 
418 	outstyle = elfedit_outstyle();
419 	if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
420 		elfedit_printf(MSG_INTL(MSG_FMT_STRTAB),
421 		    argstate->str.sec->sec_name);
422 		if (argstate->dyn.strpad.dn_seen)
423 			elfedit_printf(MSG_INTL(MSG_FMT_DYNSTRPAD),
424 			    EC_WORD(argstate->str.sec->sec_data->d_size -
425 			    argstate->dyn.strpad.dn_dyn.d_un.d_val),
426 			    EC_WORD(argstate->str.sec->sec_data->d_size - 1),
427 			    EC_WORD(argstate->dyn.strpad.dn_dyn.d_un.d_val));
428 		elfedit_printf(MSG_INTL(MSG_FMT_DUMPTITLE));
429 	}
430 
431 	str = argstate->str.sec->sec_data->d_buf;
432 	tbl_limit = str + argstate->str.sec->sec_data->d_size;
433 	ndx = argstate->str.ndx;
434 	if (argstate->argc > 0) {
435 		str += ndx;
436 		/*
437 		 * If first byte is NULL and this is the default output style,
438 		 * then we want to display the range of NULL bytes, and we
439 		 * push limit out to the last one in the sequence. Otherwise,
440 		 * just display the string.
441 		 */
442 		if ((*str == '\0') && (outstyle == ELFEDIT_OUTSTYLE_DEFAULT)) {
443 			limit = str;
444 			while (((limit + 1) < tbl_limit) &&
445 			    (*(limit + 1) == '\0'))
446 				limit++;
447 		} else {
448 			limit = str + strlen(str) + 1;
449 		}
450 	} else {
451 		/* Display the entire string table  */
452 		limit = tbl_limit;
453 	}
454 
455 
456 	while (str < limit) {
457 		Word	skip = strlen(str) + 1;
458 		Word	start_ndx;
459 
460 		if (outstyle != ELFEDIT_OUTSTYLE_DEFAULT) {
461 			elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), str);
462 			str += skip;
463 			ndx += skip;
464 			continue;
465 		}
466 
467 		start_ndx = ndx;
468 		if (*str == '\0')
469 			while (((str + 1) < limit) && (*(str + 1) == '\0')) {
470 				ndx++;
471 				str++;
472 			}
473 
474 		if (start_ndx != ndx) {
475 			(void) snprintf(index, sizeof (index),
476 			    MSG_ORIG(MSG_FMT_INDEXRANGE),
477 			    EC_XWORD(start_ndx), EC_XWORD(ndx));
478 		} else {
479 			(void) snprintf(index, sizeof (index),
480 			    MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(ndx));
481 		}
482 		elfedit_printf(MSG_ORIG(MSG_FMT_DUMPENTRY), index, str);
483 		str += skip;
484 		ndx += skip;
485 	}
486 }
487 
488 
489 /*
490  * Command body for str:set, handling the case where the 3rd
491  * argument (new-str) is present.
492  */
493 static elfedit_cmdret_t
494 cmd_body_set(ARGSTATE *argstate)
495 {
496 	elfedit_section_t	*strsec = argstate->str.sec;
497 	const char		*newstr = argstate->argv[1];
498 	Word	ndx = argstate->str.ndx;
499 	char	*oldstr;
500 	int	i, len, ncp;
501 
502 	len = strlen(newstr);
503 	ncp = len;
504 	if (!(argstate->optmask & STR_OPT_F_NOTERM))
505 		ncp++;
506 
507 	/* NULL string with no termination? Nothing to do */
508 	if (ncp == 0)
509 		return (ELFEDIT_CMDRET_NONE);
510 
511 	/* Does it fit? */
512 	if ((ndx + ncp) > strsec->sec_data->d_size)
513 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFIT),
514 		    EC_WORD(strsec->sec_shndx), strsec->sec_name,
515 		    EC_WORD(ndx), newstr);
516 
517 	/* Does it clobber the final NULL termination? */
518 	if (((ndx + ncp) == strsec->sec_data->d_size) &&
519 	    (argstate->optmask & STR_OPT_F_NOTERM))
520 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FINALNULL),
521 		    EC_WORD(strsec->sec_shndx), strsec->sec_name,
522 		    EC_WORD(ndx), newstr);
523 
524 	/*
525 	 * strtab[0] is always supposed to contain a NULL byte. You're not
526 	 * supposed to mess with it. We will carry out this operation,
527 	 * but with a debug message indicating that it is unorthodox.
528 	 */
529 	if ((ndx == 0) && (*newstr != '\0'))
530 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CHGSTR0),
531 		    EC_WORD(strsec->sec_shndx), strsec->sec_name,
532 		    EC_WORD(ndx), newstr);
533 
534 	/* Does it alter the existing value? */
535 	oldstr = ndx + (char *)strsec->sec_data->d_buf;
536 	for (i = 0; i < ncp; i++)
537 		if (newstr[i] != oldstr[i])
538 			break;
539 	if (i == ncp) {		/* No change */
540 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK),
541 		    strsec->sec_shndx, strsec->sec_name, ndx, newstr);
542 		return (ELFEDIT_CMDRET_NONE);
543 	}
544 
545 	/*
546 	 * If the new string is longer than the old one, then it will
547 	 * clobber the start of the following string. The resulting
548 	 * string table is perfectly legal, but issue a debug message
549 	 * letting the user know.
550 	 */
551 	i = strlen(oldstr);
552 	if (len > i)
553 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LONGSTR),
554 		    EC_WORD(strsec->sec_shndx), strsec->sec_name,
555 		    EC_WORD(ndx), len, i);
556 
557 	/*
558 	 * If we have strayed into the reserved part of the dynstr, then
559 	 * update DT_SUNW_STRPAD.
560 	 */
561 	if (argstate->dyn.strpad.dn_seen) {
562 		elfedit_dyn_elt_t	*strpad = &argstate->dyn.strpad;
563 		Word	new_pad_ndx = ndx + len + 1;
564 		Word	pad_ndx = argstate->str.sec->sec_data->d_size -
565 		    strpad->dn_dyn.d_un.d_val;
566 
567 		if (new_pad_ndx > pad_ndx) {
568 			elfedit_msg(ELFEDIT_MSG_DEBUG,
569 			    MSG_INTL(MSG_DEBUG_ADDDYNSTR),
570 			    EC_WORD(strsec->sec_shndx), strsec->sec_name,
571 			    EC_WORD(ndx), EC_WORD(new_pad_ndx - pad_ndx),
572 			    EC_WORD(strpad->dn_dyn.d_un.d_val),
573 			    newstr);
574 
575 			strpad->dn_dyn.d_un.d_val =
576 			    argstate->dyn.data[strpad->dn_ndx].d_un.d_val =
577 			    (argstate->str.sec->sec_data->d_size - new_pad_ndx);
578 			elfedit_modified_data(argstate->dyn.sec);
579 		}
580 	}
581 
582 
583 
584 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG),
585 	    strsec->sec_shndx, strsec->sec_name, ndx, len, oldstr, newstr);
586 	bcopy(newstr, oldstr, ncp);
587 
588 	return (ELFEDIT_CMDRET_MOD);
589 }
590 
591 
592 /*
593  * Command body for str:zero
594  */
595 static elfedit_cmdret_t
596 cmd_body_zero(ARGSTATE *argstate)
597 {
598 	elfedit_section_t	*strsec = argstate->str.sec;
599 	Word	count;
600 	Word	ndx = argstate->str.ndx;
601 	char	*oldstr = ndx + (char *)strsec->sec_data->d_buf;
602 	Word	i;
603 
604 	/* How many bytes to zero? */
605 	if (argstate->optmask & STR_OPT_F_END)
606 		count = strsec->sec_data->d_size - argstate->str.ndx;
607 	else if (argstate->argc == 2)
608 		count = elfedit_atoui_range(argstate->argv[1],
609 		    MSG_ORIG(MSG_STR_COUNT), 0,
610 		    argstate->str.sec->sec_data->d_size - argstate->str.ndx,
611 		    NULL);
612 	else
613 		count = strlen(oldstr);
614 
615 	/* Does it alter the existing value? */
616 	for (i = 0; i < count; i++)
617 		if (oldstr[i] != '\0')
618 			break;
619 	if (i == count) {		/* No change */
620 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_Z_OK),
621 		    strsec->sec_shndx, strsec->sec_name, ndx);
622 		return (ELFEDIT_CMDRET_NONE);
623 	}
624 
625 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_Z_CHG),
626 	    strsec->sec_shndx, strsec->sec_name, ndx, count);
627 	bzero(oldstr, count);
628 
629 	return (ELFEDIT_CMDRET_MOD);
630 }
631 
632 
633 /*
634  * Common body for the str: module commands.
635  *
636  * entry:
637  *	cmd - One of the STR_CMD_T_* constants listed above, specifying
638  *		which command to implement.
639  *	obj_state, argc, argv - Standard command arguments
640  */
641 static elfedit_cmdret_t
642 cmd_body(STR_CMD_T cmd, elfedit_obj_state_t *obj_state,
643     int argc, const char *argv[])
644 {
645 	ARGSTATE		argstate;
646 	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
647 	int			print_only;
648 
649 	process_args(obj_state, argc, argv, cmd, &argstate, &print_only);
650 
651 	/*
652 	 * If this call call does not change data, display the current
653 	 * value(s) and return.
654 	 */
655 	if (print_only) {
656 		print_strtab(0, &argstate);
657 		return (ELFEDIT_CMDRET_NONE);
658 	}
659 
660 	switch (cmd) {
661 	/* NOTE: STR_CMD_T_DUMP can't get here --- it's always print_only */
662 
663 	case STR_CMD_T_SET:
664 		ret = cmd_body_set(&argstate);
665 		break;
666 
667 	case STR_CMD_T_ADD:
668 		argstate.str.ndx = elfedit_strtab_insert(obj_state,
669 		    argstate.str.sec, argstate.dyn.sec, argstate.argv[0]);
670 		break;
671 
672 	case STR_CMD_T_ZERO:
673 		ret = cmd_body_zero(&argstate);
674 		break;
675 	}
676 
677 	/*
678 	 * If we modified the strtab section, tell libelf.
679 	 */
680 	if (ret == ELFEDIT_CMDRET_MOD)
681 		elfedit_modified_data(argstate.str.sec);
682 
683 	/* Do autoprint */
684 	print_strtab(1, &argstate);
685 
686 	return (ret);
687 }
688 
689 
690 
691 
692 /*
693  * Command completion functions for the various commands
694  */
695 
696 static void
697 add_shtyp_match(Word sh_type, void *cpldata)
698 {
699 	char		buf[128];
700 	const char	*s;
701 	char		*s2;
702 
703 	s = elfedit_atoconst_value_to_str(ELFEDIT_CONST_SHT, sh_type, 0);
704 	elfedit_cpl_match(cpldata, s, 1);
705 
706 	/*
707 	 * To get the informal tag names that are lowercase
708 	 * and lack the leading SHT_, we copy the string we
709 	 * have into a buffer and process it.
710 	 */
711 	if (strlen(s) < 4)
712 		return;
713 	(void) strlcpy(buf, s + 4, sizeof (buf));
714 	for (s2 = buf; *s2 != '\0'; s2++)
715 		if (isupper(*s2))
716 			*s2 = tolower(*s2);
717 	elfedit_cpl_match(cpldata, buf, 1);
718 }
719 
720 /*
721  * Handle filling in the values for -shnam, -shndx, and -shtyp options.
722  */
723 /*ARGSUSED*/
724 static void
725 cpl_sh_opt(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
726     const char *argv[], int num_opt)
727 {
728 	enum { NAME, INDEX, TYPE }	op;
729 	elfedit_section_t		*sec;
730 	Word 	ndx;
731 
732 	if ((argc != num_opt) || (argc < 2))
733 		return;
734 
735 	if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNAM)) == 0) {
736 		op = NAME;
737 	} else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0) {
738 		op = INDEX;
739 
740 	} else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 0) {
741 		op = TYPE;
742 
743 		if (obj_state == NULL) {	 /* No object available */
744 			elfedit_atoui_sym_t *atoui_sym;
745 
746 			atoui_sym = elfedit_const_to_atoui(ELFEDIT_CONST_SHT);
747 			for (; atoui_sym->sym_name != NULL; atoui_sym++)
748 				if (shtype_to_strtab(atoui_sym->sym_value) !=
749 				    SHTOSTR_NONE)
750 					elfedit_cpl_match(cpldata,
751 					    atoui_sym->sym_name, 1);
752 		}
753 	} else {
754 		return;
755 	}
756 
757 	if (obj_state == NULL)	 /* No object available */
758 		return;
759 
760 	/*
761 	 * Loop over the section headers and supply command completion
762 	 * for the items in the file that can yield a string table.
763 	 */
764 	sec = obj_state->os_secarr;
765 	for (ndx = 0; ndx < obj_state->os_shnum; ndx++, sec++) {
766 		Word sh_type = sec->sec_shdr->sh_type;
767 
768 		if (shtype_to_strtab(sh_type) == SHTOSTR_NONE)
769 			continue;
770 
771 		switch (op) {
772 		case NAME:
773 			elfedit_cpl_match(cpldata, sec->sec_name, 0);
774 			break;
775 		case INDEX:
776 			{
777 				char index[MAXNDXSIZE];
778 
779 				(void) snprintf(index, sizeof (index),
780 				    MSG_ORIG(MSG_FMT_WORDVAL),
781 				    sec->sec_shndx);
782 				elfedit_cpl_match(cpldata, index, 1);
783 			}
784 			break;
785 		case TYPE:
786 			add_shtyp_match(sh_type, cpldata);
787 			break;
788 		}
789 	}
790 }
791 
792 
793 /*
794  * Most of the commands accept an -shXXX option for the string table
795  * and a string first argument. This routine examines which argument
796  * is being processed, and supplies completion for these items.
797  */
798 static void
799 cpl_sec_str(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
800     const char *argv[], int num_opt)
801 {
802 	const char		*str, *limit;
803 	elfedit_section_t	*sec;
804 	Word			strtab_ndx;
805 	Word			ndx;
806 
807 	/* Handle -shXXX options */
808 	cpl_sh_opt(obj_state, cpldata, argc, argv, num_opt);
809 
810 	/* Without object state, there's no data to work from */
811 	if (obj_state == NULL)
812 		return;
813 
814 	/* If not first plain arg, return */
815 	if (argc != (num_opt + 1))
816 		return;
817 
818 	/*
819 	 * Look at the options, looking for two things:
820 	 *	1) A -shXXX option specifying a section. If so, turn that
821 	 *		into a section index if possible.
822 	 *	2) Was -strndx used? If so, we are looking at an integer
823 	 *		value and have nothing to complete.
824 	 */
825 	strtab_ndx = obj_state->os_ehdr->e_shstrndx;
826 	for (ndx = 0; ndx < num_opt; ndx++) {
827 		if (strcmp(argv[ndx], MSG_ORIG(MSG_STR_MINUS_STRNDX)) == 0)
828 			return;
829 
830 		if ((ndx+1) < num_opt) {
831 			if (strcmp(argv[ndx],
832 			    MSG_ORIG(MSG_STR_MINUS_SHNAM)) == 0) {
833 				Word		i;
834 
835 				for (i = 1; i < obj_state->os_shnum; i++)
836 					if (strcmp(obj_state->os_secarr[i].
837 					    sec_name, argv[ndx+1]) == 0) {
838 						strtab_ndx = i;
839 						break;
840 					}
841 			} else if (strcmp(argv[ndx],
842 			    MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0) {
843 				elfedit_atoui_t val;
844 
845 				if (elfedit_atoui2(argv[ndx+1], NULL,
846 				    &val) != 0)
847 					strtab_ndx = val;
848 			} else if (strcmp(argv[ndx],
849 			    MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 0) {
850 				elfedit_atoui_t	sh_type;
851 				Word		i;
852 
853 				if (elfedit_atoconst2(argv[ndx+1],
854 				    ELFEDIT_CONST_SHT, &sh_type) == 0)
855 					continue;
856 				for (i = 1; i < obj_state->os_shnum; i++)
857 					if (obj_state->os_secarr[i].sec_shdr->
858 					    sh_type == sh_type) {
859 						strtab_ndx = i;
860 						break;
861 					}
862 			}
863 		}
864 	}
865 
866 	/*
867 	 * Locate and validate the string table. In the case where
868 	 * a non-string table section is given that references a string
869 	 * table, we will use the referenced table.
870 	 */
871 	strtab_ndx = shndx_to_strtab(obj_state, strtab_ndx);
872 	if ((strtab_ndx >= obj_state->os_shnum) ||
873 	    (obj_state->os_secarr[strtab_ndx].sec_shdr->sh_type != SHT_STRTAB))
874 		return;
875 	sec = &obj_state->os_secarr[strtab_ndx];
876 
877 	str = sec->sec_data->d_buf;
878 	limit = str + sec->sec_data->d_size;
879 	while (str < limit) {
880 		if (*str != '\0')
881 			elfedit_cpl_match(cpldata, str, 0);
882 		str += strlen(str) + 1;
883 	}
884 }
885 
886 
887 
888 /*
889  * Implementation functions for the commands
890  */
891 static elfedit_cmdret_t
892 cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
893 {
894 	return (cmd_body(STR_CMD_T_DUMP, obj_state, argc, argv));
895 }
896 
897 static elfedit_cmdret_t
898 cmd_set(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
899 {
900 	return (cmd_body(STR_CMD_T_SET, obj_state, argc, argv));
901 }
902 
903 static elfedit_cmdret_t
904 cmd_add(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
905 {
906 	return (cmd_body(STR_CMD_T_ADD, obj_state, argc, argv));
907 }
908 
909 static elfedit_cmdret_t
910 cmd_zero(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
911 {
912 	return (cmd_body(STR_CMD_T_ZERO, obj_state, argc, argv));
913 }
914 
915 
916 
917 /*ARGSUSED*/
918 elfedit_module_t *
919 elfedit_init(elfedit_module_version_t version)
920 {
921 	/* str:dump */
922 	static const char *name_dump[] = {
923 	    MSG_ORIG(MSG_CMD_DUMP),
924 	    MSG_ORIG(MSG_STR_EMPTY),	/* "" makes this the default command */
925 	    NULL
926 	};
927 	static elfedit_cmd_optarg_t opt_dump[] = {
928 		{ ELFEDIT_STDOA_OPT_O, NULL,
929 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
930 		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
931 		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
932 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
933 		    STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
934 		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
935 		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
936 		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
937 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
938 		    STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
939 		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
940 		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
941 		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
942 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
943 		    STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
944 		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
945 		{ MSG_ORIG(MSG_STR_MINUS_STRNDX),
946 		    /* MSG_INTL(MSG_OPTDESC_STRNDX) */
947 		    ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
948 		    STR_OPT_F_STRNDX, 0 },
949 		{ NULL }
950 	};
951 	static elfedit_cmd_optarg_t arg_dump[] = {
952 		{ MSG_ORIG(MSG_STR_STRING),
953 		    /* MSG_INTL(MSG_A1_STRING) */
954 		    ELFEDIT_I18NHDL(MSG_A1_STRING),
955 		    ELFEDIT_CMDOA_F_OPT },
956 		{ NULL }
957 	};
958 
959 	/* str:set */
960 	static const char *name_set[] = {
961 	    MSG_ORIG(MSG_CMD_SET), NULL };
962 	static elfedit_cmd_optarg_t opt_set[] = {
963 		{ ELFEDIT_STDOA_OPT_O, NULL,
964 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
965 		{ MSG_ORIG(MSG_STR_MINUS_NOTERM),
966 		    /* MSG_INTL(MSG_OPTDESC_NOTERM) */
967 		    ELFEDIT_I18NHDL(MSG_OPTDESC_NOTERM), 0,
968 		    STR_OPT_F_NOTERM, 0 },
969 		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
970 		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
971 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
972 		    STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
973 		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
974 		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
975 		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
976 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
977 		    STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
978 		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
979 		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
980 		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
981 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
982 		    STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
983 		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
984 		{ MSG_ORIG(MSG_STR_MINUS_STRNDX),
985 		    /* MSG_INTL(MSG_OPTDESC_STRNDX) */
986 		    ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
987 		    STR_OPT_F_STRNDX, 0 },
988 		{ NULL }
989 	};
990 	static elfedit_cmd_optarg_t arg_set[] = {
991 		{ MSG_ORIG(MSG_STR_STRING),
992 		    /* MSG_INTL(MSG_A1_STRING) */
993 		    ELFEDIT_I18NHDL(MSG_A1_STRING),
994 		    0 },
995 		{ MSG_ORIG(MSG_STR_NEWSTRING),
996 		    /* MSG_INTL(MSG_A2_NEWSTRING) */
997 		    ELFEDIT_I18NHDL(MSG_A2_NEWSTRING),
998 		    ELFEDIT_CMDOA_F_OPT },
999 		{ NULL }
1000 	};
1001 
1002 	/* str:add */
1003 	static const char *name_add[] = {
1004 	    MSG_ORIG(MSG_CMD_ADD), NULL };
1005 	static elfedit_cmd_optarg_t opt_add[] = {
1006 		{ ELFEDIT_STDOA_OPT_O, NULL,
1007 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
1008 		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
1009 		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
1010 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
1011 		    STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
1012 		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
1013 		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
1014 		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
1015 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
1016 		    STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
1017 		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
1018 		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
1019 		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
1020 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
1021 		    STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
1022 		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
1023 		{ NULL }
1024 	};
1025 	static elfedit_cmd_optarg_t arg_add[] = {
1026 		{ MSG_ORIG(MSG_STR_NEWSTRING),
1027 		    /* MSG_INTL(MSG_A1_NEWSTRING) */
1028 		    ELFEDIT_I18NHDL(MSG_A1_NEWSTRING),
1029 		    0 },
1030 		{ NULL }
1031 	};
1032 
1033 	/* str:zero */
1034 	static const char *name_zero[] = {
1035 	    MSG_ORIG(MSG_CMD_ZERO), NULL };
1036 	static elfedit_cmd_optarg_t opt_zero[] = {
1037 		{ ELFEDIT_STDOA_OPT_O, NULL,
1038 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
1039 		{ MSG_ORIG(MSG_STR_MINUS_SHNAM),
1040 		    /* MSG_INTL(MSG_OPTDESC_SHNAM) */
1041 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
1042 		    STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
1043 		{ MSG_ORIG(MSG_STR_NAME), NULL, 0 },
1044 		{ MSG_ORIG(MSG_STR_MINUS_SHNDX),
1045 		    /* MSG_INTL(MSG_OPTDESC_SHNDX) */
1046 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
1047 		    STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
1048 		{ MSG_ORIG(MSG_STR_INDEX), NULL, 0 },
1049 		{ MSG_ORIG(MSG_STR_MINUS_SHTYP),
1050 		    /* MSG_INTL(MSG_OPTDESC_SHTYP) */
1051 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
1052 		    STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
1053 		{ MSG_ORIG(MSG_STR_TYPE), NULL, 0 },
1054 		{ MSG_ORIG(MSG_STR_MINUS_STRNDX),
1055 		    /* MSG_INTL(MSG_OPTDESC_STRNDX) */
1056 		    ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
1057 		    STR_OPT_F_STRNDX, 0 },
1058 		{ MSG_ORIG(MSG_STR_MINUS_END),
1059 		    /* MSG_INTL(MSG_OPTDESC_END) */
1060 		    ELFEDIT_I18NHDL(MSG_OPTDESC_END), 0,
1061 		    STR_OPT_F_END, 0 },
1062 		{ NULL }
1063 	};
1064 	static elfedit_cmd_optarg_t arg_zero[] = {
1065 		{ MSG_ORIG(MSG_STR_STRING),
1066 		    /* MSG_INTL(MSG_A1_STRING) */
1067 		    ELFEDIT_I18NHDL(MSG_A1_STRING),
1068 		    0 },
1069 		{ MSG_ORIG(MSG_STR_COUNT),
1070 		    /* MSG_INTL(MSG_A2_COUNT) */
1071 		    ELFEDIT_I18NHDL(MSG_A2_COUNT),
1072 		    ELFEDIT_CMDOA_F_OPT },
1073 		{ NULL }
1074 	};
1075 
1076 
1077 	static elfedit_cmd_t cmds[] = {
1078 		/* str:dump */
1079 		{ cmd_dump, cpl_sec_str, name_dump,
1080 		    /* MSG_INTL(MSG_DESC_DUMP) */
1081 		    ELFEDIT_I18NHDL(MSG_DESC_DUMP),
1082 		    /* MSG_INTL(MSG_HELP_DUMP) */
1083 		    ELFEDIT_I18NHDL(MSG_HELP_DUMP),
1084 		    opt_dump, arg_dump },
1085 
1086 		/* str:set */
1087 		{ cmd_set, cpl_sec_str, name_set,
1088 		    /* MSG_INTL(MSG_DESC_SET) */
1089 		    ELFEDIT_I18NHDL(MSG_DESC_SET),
1090 		    /* MSG_INTL(MSG_HELP_SET) */
1091 		    ELFEDIT_I18NHDL(MSG_HELP_SET),
1092 		    opt_set, arg_set },
1093 
1094 		/* str:add */
1095 		{ cmd_add, cpl_sh_opt, name_add,
1096 		    /* MSG_INTL(MSG_DESC_ADD) */
1097 		    ELFEDIT_I18NHDL(MSG_DESC_ADD),
1098 		    /* MSG_INTL(MSG_HELP_ADD) */
1099 		    ELFEDIT_I18NHDL(MSG_HELP_ADD),
1100 		    opt_add, arg_add },
1101 
1102 		/* str:zero */
1103 		{ cmd_zero, cpl_sec_str, name_zero,
1104 		    /* MSG_INTL(MSG_DESC_ZERO) */
1105 		    ELFEDIT_I18NHDL(MSG_DESC_ZERO),
1106 		    /* MSG_INTL(MSG_HELP_ZERO) */
1107 		    ELFEDIT_I18NHDL(MSG_HELP_ZERO),
1108 		    opt_zero, arg_zero },
1109 
1110 		{ NULL }
1111 	};
1112 
1113 	static elfedit_module_t module = {
1114 	    ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_NAME),
1115 	    /* MSG_INTL(MSG_MOD_DESC) */
1116 	    ELFEDIT_I18NHDL(MSG_MOD_DESC),
1117 	    cmds, mod_i18nhdl_to_str };
1118 
1119 	return (&module);
1120 }
1121