xref: /titanic_50/usr/src/cmd/sgs/elfedit/modules/common/dyn.c (revision fc80c0dfb0c877aee828d778ea32b77fcf7b1ef4)
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	<ctype.h>
29 #include	<machdep.h>
30 #include	<elfedit.h>
31 #include	<sys/elf_SPARC.h>
32 #include	<strings.h>
33 #include	<debug.h>
34 #include	<conv.h>
35 #include	<dyn_msg.h>
36 
37 
38 /*
39  * Dynamic section
40  */
41 
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 	/* Dump command, used as module default to display dynamic section */
52 	DYN_CMD_T_DUMP =	0,	/* dyn:dump */
53 
54 	/* Commands that do not correspond directly to a specific DT tag */
55 	DYN_CMD_T_TAG =		1,	/* dyn:tag */
56 	DYN_CMD_T_VALUE =	2,	/* dyn:value */
57 	DYN_CMD_T_DELETE =	3,	/* dyn:delete */
58 	DYN_CMD_T_MOVE =	4,	/* dyn:shift */
59 
60 	/* Commands that embody tag specific knowledge */
61 	DYN_CMD_T_RUNPATH =	5,	/* dyn:runpath/rpath */
62 	DYN_CMD_T_POSFLAG1 =	6,	/* dyn:posflag1 */
63 	DYN_CMD_T_FLAGS =	7,	/* dyn:flags */
64 	DYN_CMD_T_FLAGS1 =	8,	/* dyn:flags1 */
65 	DYN_CMD_T_FEATURE1 =	9,	/* dyn:feature1 */
66 	DYN_CMD_T_CHECKSUM =	10	/* dyn:checksum */
67 } DYN_CMD_T;
68 
69 
70 
71 #ifndef _ELF64
72 /*
73  * We supply this function for the msg module
74  */
75 const char *
76 _dyn_msg(Msg mid)
77 {
78 	return (gettext(MSG_ORIG(mid)));
79 }
80 #endif
81 
82 
83 /*
84  * This function is supplied to elfedit through our elfedit_module_t
85  * definition. It translates the opaque elfedit_i18nhdl_t handles
86  * in our module interface into the actual strings for elfedit to
87  * use.
88  *
89  * note:
90  *	This module uses Msg codes for its i18n handle type.
91  *	So the translation is simply to use MSG_INTL() to turn
92  *	it into a string and return it.
93  */
94 static const char *
95 mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
96 {
97 	Msg msg = (Msg)hdl;
98 
99 	return (MSG_INTL(msg));
100 }
101 
102 
103 
104 /*
105  * The dyn_opt_t enum specifies a bit value for every optional
106  * argument allowed by a command in this module.
107  */
108 typedef enum {
109 	DYN_OPT_F_ADD =		1,	/* -add: Add new elt rather than */
110 					/*	modifying an existing one */
111 	DYN_OPT_F_AND =		2,	/* -and: AND (&) values to dest */
112 	DYN_OPT_F_CMP =		4,	/* -cmp: Complement (~) values */
113 	DYN_OPT_F_DYNNDX =	8,	/* -dynndx: elt is tag index, */
114 					/*	not name */
115 	DYN_OPT_F_OR =		16,	/* -or: OR (|) values to dest */
116 	DYN_OPT_F_STRVAL =	32	/* -s: value is string, not integer */
117 } dyn_opt_t;
118 
119 
120 /*
121  * A variable of type ARGSTATE is used by each command to maintain
122  * information about the arguments and related things. It is
123  * initialized by process_args(), and used by the other routines.
124  */
125 typedef struct {
126 	elfedit_obj_state_t	*obj_state;
127 	elfedit_section_t	*strsec;	/* Dynamic string table ref */
128 	struct {
129 		elfedit_section_t *sec;		/* Dynamic section reference */
130 		Dyn	*data;			/* Start dynamic section data */
131 		Word	num;			/* # dynamic elts */
132 		Word	null_ndx;		/* Index of first DT_NULL */
133 		Word	num_null_ndx;		/* # of DT_NULL elements */
134 	} dyn;
135 	dyn_opt_t		optmask;   	/* Mask of options used */
136 	int			argc;		/* # of plain arguments */
137 	const char		**argv;		/* Plain arguments */
138 } ARGSTATE;
139 
140 
141 
142 /*
143  * Set argstate null_ndx field for current dynamic area
144  */
145 static void
146 set_null_ndx(ARGSTATE *argstate)
147 {
148 	Word	num, null_ndx;
149 
150 	num = argstate->dyn.num;
151 	argstate->dyn.num_null_ndx = 0;
152 	for (null_ndx = 0; null_ndx < num; null_ndx++)
153 		if (argstate->dyn.data[null_ndx].d_tag == DT_NULL) {
154 			argstate->dyn.num_null_ndx++;
155 			break;
156 		}
157 	argstate->dyn.null_ndx = null_ndx;
158 
159 	/* Count the number of remaining DT_NULL items */
160 	for (; null_ndx < num; null_ndx++)
161 		if (argstate->dyn.data[null_ndx].d_tag == DT_NULL)
162 			argstate->dyn.num_null_ndx++;
163 }
164 
165 
166 /*
167  * Convert the first available DT_NULL slot in the dynamic section
168  * into something else.
169  *
170  * entry:
171  *	argstate - Argument state block
172  *	d_tag, d_val - Values to be set in new element
173  *
174  * exit:
175  *	If an extra DT_NULL slot is available, a debug message is
176  *	issued, the slot is converted to its new use, and the argstate
177  *	block state related to DT_NULL slots is updated.
178  *
179  *	if no extra DT_NULL slot is present, an error is issued and
180  *	this routine does not return to the caller.
181  */
182 static Word
183 convert_dt_null(ARGSTATE *argstate, Word d_tag, Xword d_val)
184 {
185 	Conv_inv_buf_t inv_buf;
186 	Word	ndx;
187 	Dyn	*dyn;
188 
189 	/* If we lack an extra element, we can't continue */
190 	if (argstate->dyn.num_null_ndx <= 1)
191 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOEXTRANULL),
192 		    EC_WORD(argstate->dyn.sec->sec_shndx),
193 		    argstate->dyn.sec->sec_name);
194 
195 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CONVNULL),
196 	    EC_WORD(argstate->dyn.sec->sec_shndx), argstate->dyn.sec->sec_name,
197 	    EC_WORD(argstate->dyn.null_ndx), conv_dyn_tag(d_tag,
198 	    argstate->obj_state->os_ehdr->e_machine, 0, &inv_buf));
199 
200 	ndx = argstate->dyn.null_ndx;
201 	dyn = &argstate->dyn.data[ndx];
202 	dyn->d_tag = d_tag;
203 	dyn->d_un.d_val = d_val;
204 
205 	/* Recompute the DT_NULL situation */
206 	set_null_ndx(argstate);
207 
208 	return (ndx);
209 }
210 
211 
212 /*
213  * Standard argument processing for dyn module
214  *
215  * entry
216  *	obj_state, argc, argv - Standard command arguments
217  *	argstate - Address of ARGSTATE block to be initialized
218  *
219  * exit:
220  *	On success, *argstate is initialized. On error,
221  *	an error is issued and this routine does not return.
222  */
223 static void
224 process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[],
225     ARGSTATE *argstate)
226 {
227 	elfedit_getopt_state_t	getopt_state;
228 	elfedit_getopt_ret_t	*getopt_ret;
229 
230 	bzero(argstate, sizeof (*argstate));
231 	argstate->obj_state = obj_state;
232 
233 	elfedit_getopt_init(&getopt_state, &argc, &argv);
234 
235 	/* Add each new option to the options mask */
236 	while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL)
237 		argstate->optmask |= getopt_ret->gor_idmask;
238 
239 	/* If there may be an arbitrary amount of output, use a pager */
240 	if (argc == 0)
241 		elfedit_pager_init();
242 
243 	/* Return the updated values of argc/argv */
244 	argstate->argc = argc;
245 	argstate->argv = argv;
246 
247 	/* Locate the dynamic section, and the assocated string table */
248 	argstate->dyn.sec = elfedit_sec_getdyn(obj_state, &argstate->dyn.data,
249 	    &argstate->dyn.num);
250 	argstate->strsec = elfedit_sec_getstr(obj_state,
251 	    argstate->dyn.sec->sec_shdr->sh_link);
252 
253 	/* Index of first DT_NULL */
254 	set_null_ndx(argstate);
255 }
256 
257 
258 
259 /*
260  * Print ELF header values, taking the calling command, and output style
261  * into account.
262  *
263  * entry:
264  *	cmd - DYN_CMD_T_* value giving identify of caller
265  *	autoprint - If True, output is only produced if the elfedit
266  *		autoprint flag is set. If False, output is always produced.
267  *	argstate - Argument state block
268  *	print_type - Specifies which dynamic elements to display.
269  *	ndx = If print_type is PRINT_DYN_T_NDX, displays the index specified.
270  *		Otherwise ignored.
271  */
272 typedef enum {
273 	PRINT_DYN_T_ALL =	0,	/* Show all indexes */
274 	PRINT_DYN_T_NDX =	1,	/* Show dynamic[arg] only */
275 	PRINT_DYN_T_TAG =	2,	/* Show all elts with tag type */
276 					/*	given by arg */
277 	PRINT_DYN_T_RUNPATH =	3	/* Show all runpath/rpath elts */
278 
279 } PRINT_DYN_T;
280 
281 static void
282 print_dyn(DYN_CMD_T cmd, int autoprint, ARGSTATE *argstate,
283     PRINT_DYN_T print_type, Word arg)
284 {
285 	elfedit_outstyle_t	outstyle;
286 	Conv_fmt_flags_t	flags_fmt_flags;
287 	Word	end_ndx, cnt, ndx, printed = 0;
288 	Dyn	*dyn;
289 	int	header_done = 0;
290 	Xword	last_d_val;
291 
292 	if (autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0))
293 		return;
294 
295 	/*
296 	 * Pick an output style. dyn:dump is required to use the default
297 	 * style. The other commands use the current output style.
298 	 */
299 	outstyle = (cmd == DYN_CMD_T_DUMP) ?
300 	    ELFEDIT_OUTSTYLE_DEFAULT : elfedit_outstyle();
301 
302 	/*
303 	 * When using the simple output style, omit the
304 	 * brackets from around the values.
305 	 */
306 	flags_fmt_flags = (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) ?
307 	    CONV_FMT_NOBKT : 0;
308 
309 	/* How many elements do we examine? */
310 	if (print_type == PRINT_DYN_T_NDX) {
311 		if (arg >= argstate->dyn.num)
312 			return;		/* Out of range */
313 		ndx = arg;
314 		cnt = 1;
315 	} else {
316 		ndx = 0;
317 		cnt = argstate->dyn.num;
318 	}
319 
320 	dyn = &argstate->dyn.data[ndx];
321 	for (; cnt--; dyn++, ndx++) {
322 		union {
323 			Conv_dyn_flag_buf_t	flag;
324 			Conv_dyn_flag1_buf_t	flag1;
325 			Conv_dyn_posflag1_buf_t	posflag1;
326 			Conv_dyn_feature1_buf_t	feature1;
327 		} c_buf;
328 		const char	*name;
329 
330 		/*
331 		 * If we are only displaying certain tag types and
332 		 * this isn't one of those, move on to next element.
333 		 */
334 		switch (print_type) {
335 		case PRINT_DYN_T_TAG:
336 			if (dyn->d_tag != arg)
337 				continue;
338 			break;
339 		case PRINT_DYN_T_RUNPATH:
340 			if ((dyn->d_tag != DT_RPATH) &&
341 			    (dyn->d_tag != DT_RUNPATH))
342 				continue;
343 			break;
344 		}
345 
346 		/*
347 		 * Print the information numerically, and if possible
348 		 * as a string.
349 		 */
350 		name = NULL;
351 		switch (dyn->d_tag) {
352 		case DT_NULL:
353 			if (!((outstyle == ELFEDIT_OUTSTYLE_DEFAULT) &&
354 			    (print_type == PRINT_DYN_T_ALL) &&
355 			    (dyn->d_un.d_val == 0)))
356 				break;
357 			end_ndx = ndx;
358 			/*
359 			 * Special case: DT_NULLs can come in groups
360 			 * that we prefer to reduce to a single line.
361 			 */
362 			while ((end_ndx < (argstate->dyn.num - 1)) &&
363 			    ((dyn + 1)->d_tag == DT_NULL) &&
364 			    ((dyn + 1)->d_un.d_val == 0)) {
365 				dyn++;
366 				end_ndx++;
367 				cnt--;
368 			}
369 			if (header_done == 0) {
370 				header_done = 1;
371 				Elf_dyn_title(0);
372 			}
373 			Elf_dyn_null_entry(0, dyn, ndx, end_ndx);
374 			ndx = end_ndx;
375 			printed = 1;
376 			last_d_val = dyn->d_un.d_val;
377 			continue;
378 
379 		/*
380 		 * Print the information numerically, and if possible
381 		 * as a string.
382 		 */
383 		case DT_NEEDED:
384 		case DT_SONAME:
385 		case DT_FILTER:
386 		case DT_AUXILIARY:
387 		case DT_CONFIG:
388 		case DT_RPATH:
389 		case DT_RUNPATH:
390 		case DT_USED:
391 		case DT_DEPAUDIT:
392 		case DT_AUDIT:
393 		case DT_SUNW_AUXILIARY:
394 		case DT_SUNW_FILTER:
395 			name = elfedit_offset_to_str(argstate->strsec,
396 			    dyn->d_un.d_val, ELFEDIT_MSG_DEBUG, 0);
397 			break;
398 
399 		case DT_FLAGS:
400 			name = conv_dyn_flag(dyn->d_un.d_val,
401 			    flags_fmt_flags, &c_buf.flag);
402 			break;
403 		case DT_FLAGS_1:
404 			name = conv_dyn_flag1(dyn->d_un.d_val,
405 			    flags_fmt_flags, &c_buf.flag1);
406 			break;
407 		case DT_POSFLAG_1:
408 			name = conv_dyn_posflag1(dyn->d_un.d_val,
409 			    flags_fmt_flags, &c_buf.posflag1);
410 			break;
411 		case DT_FEATURE_1:
412 			name = conv_dyn_feature1(dyn->d_un.d_val,
413 			    flags_fmt_flags, &c_buf.feature1);
414 			break;
415 		case DT_DEPRECATED_SPARC_REGISTER:
416 			name = MSG_INTL(MSG_STR_DEPRECATED);
417 			break;
418 		}
419 
420 		if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
421 			if (header_done == 0) {
422 				header_done = 1;
423 				Elf_dyn_title(0);
424 			}
425 			if (name == NULL)
426 				name = MSG_ORIG(MSG_STR_EMPTY);
427 			Elf_dyn_entry(0, dyn, ndx, name,
428 			    argstate->obj_state->os_ehdr->e_machine);
429 		} else {
430 			/*
431 			 * In simple or numeric mode under a print type
432 			 * that is based on tag type rather than on index,
433 			 * quietly: If we've already printed this value,
434 			 * don't print it again. A common example of this
435 			 * is PRINT_DYN_T_RUNPATH when both DT_RPATH and
436 			 * DT_RUNPATH are present with the same value.
437 			 */
438 			switch (print_type) {
439 			case PRINT_DYN_T_TAG:
440 			case PRINT_DYN_T_RUNPATH:
441 				if (printed && (last_d_val == dyn->d_un.d_val))
442 					continue;
443 			}
444 
445 			if ((name != NULL) &&
446 			    (outstyle == ELFEDIT_OUTSTYLE_SIMPLE)) {
447 				elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), name);
448 			} else {
449 				elfedit_printf(MSG_ORIG(MSG_FMT_HEXXWORDNL),
450 				    dyn->d_un.d_val);
451 			}
452 		}
453 		printed = 1;
454 		last_d_val = dyn->d_un.d_val;
455 	}
456 
457 	/*
458 	 * If nothing was output under the print types that are
459 	 * based on tag type, issue an error saying it doesn't exist.
460 	 */
461 	if (!printed) {
462 		if (print_type == PRINT_DYN_T_TAG) {
463 			Conv_inv_buf_t inv_buf;
464 
465 			elfedit_msg(ELFEDIT_MSG_ERR,
466 			    MSG_INTL(MSG_ERR_NODYNELT),
467 			    EC_WORD(argstate->dyn.sec->sec_shndx),
468 			    argstate->dyn.sec->sec_name, conv_dyn_tag(arg,
469 			    argstate->obj_state->os_ehdr->e_machine,
470 			    0, &inv_buf));
471 		}
472 
473 		if (print_type == PRINT_DYN_T_RUNPATH)
474 			elfedit_msg(ELFEDIT_MSG_ERR,
475 			    MSG_INTL(MSG_ERR_NORUNPATH),
476 			    EC_WORD(argstate->dyn.sec->sec_shndx),
477 			    argstate->dyn.sec->sec_name);
478 	}
479 }
480 
481 
482 /*
483  * Process the elt argument: This will be a tag type if -dynndx is
484  * not present and this is a print request. It will be an index otherwise.
485  *
486  * entry:
487  *	argstate - Argument state block
488  *	arg - Argument string to be converted into an index
489  *	argname - String giving the name by which the argument is
490  *		referred in the online help for the command.
491  *	print_request - True if the command is to print the current
492  *		value(s) and return without changing anything.
493  *	print_type - Address of variable containing PRINT_DYN_T_
494  *		code specifying how the elements will be displayed.
495  *
496  * exit:
497  *	If print_request is False: arg is converted into an integer value.
498  *	If -dynndx was used, we convert it into an integer. If it was not
499  *	used, then arg is a tag name --- we find the first dynamic entry
500  *	that matches. If no entry matches, and there is an extra DT_NULL,
501  *	it is added. Otherwise an error is issued. *print_type is set
502  *	to PRINT_DYN_T_NDX.
503  *
504  *	If print_request is True: If -dynndx was used, arg is converted into
505  *	an integer value, *print_type is set to PRINT_DYN_T_NDX, and
506  *	the value is returned. If -dynndx was not used, *print_type is set to
507  *	PRINT_DYN_T_TAG, and the tag value is returned.
508  */
509 static Word
510 arg_to_index(ARGSTATE *argstate, const char *arg, const char *argname,
511     int print_request, PRINT_DYN_T *print_type)
512 {
513 	Word	ndx, dt_value;
514 
515 
516 	/* Assume we are returning an index, alter as needed below */
517 	*print_type = PRINT_DYN_T_NDX;
518 
519 	/* If -dynndx was used, this is a simple numeric index */
520 	if ((argstate->optmask & DYN_OPT_F_DYNNDX) != 0)
521 		return ((Word) elfedit_atoui_range(arg, argname, 0,
522 		    argstate->dyn.num - 1, NULL));
523 
524 	/* The argument is a DT_ tag type, not a numeric index */
525 	dt_value = (Word) elfedit_atoconst(arg, ELFEDIT_CONST_DT);
526 
527 	/*
528 	 * If this is a printing request, then we let print_dyn() show
529 	 * all the items with this tag type.
530 	 */
531 	if (print_request) {
532 		*print_type = PRINT_DYN_T_TAG;
533 		return (dt_value);
534 	}
535 
536 	/* Locate the first entry with the given tag type */
537 	for (ndx = 0; ndx < argstate->dyn.num; ndx++) {
538 		if (argstate->dyn.data[ndx].d_tag == dt_value) {
539 			elfedit_msg(ELFEDIT_MSG_DEBUG,
540 			    MSG_INTL(MSG_DEBUG_DT2NDX),
541 			    EC_WORD(argstate->dyn.sec->sec_shndx),
542 			    argstate->dyn.sec->sec_name, EC_WORD(ndx), arg);
543 			return (ndx);
544 		}
545 	}
546 
547 	/* Not found. Can we create one? */
548 	if (argstate->dyn.num_null_ndx > 1)
549 		return (convert_dt_null(argstate, dt_value, 0));
550 
551 	/* No room to create one, so we're out of options and must fail */
552 	elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NODTELT),
553 	    EC_WORD(argstate->dyn.sec->sec_shndx),
554 	    argstate->dyn.sec->sec_name, arg);
555 
556 	/*NOTREACHED*/
557 	return (0);		/* For lint */
558 }
559 
560 
561 /*
562  * Called by cmd_body() for dyn:value. Implements the core functionality
563  * for that command.
564  *
565  * This routine expects that both the index and value arguments are
566  * present.
567  */
568 static elfedit_cmdret_t
569 cmd_body_value(ARGSTATE *argstate, Word *ret_ndx)
570 {
571 	elfedit_section_t	*dynsec = argstate->dyn.sec;
572 	elfedit_section_t	*strsec = argstate->strsec;
573 	elfedit_dyn_elt_t	strpad_elt;
574 	Word	i;
575 	Dyn	*dyn = argstate->dyn.data;
576 	Word	numdyn = argstate->dyn.num;
577 	int	minus_add = ((argstate->optmask & DYN_OPT_F_ADD) != 0);
578 	int	minus_s = ((argstate->optmask & DYN_OPT_F_STRVAL) != 0);
579 	int	minus_dynndx = ((argstate->optmask & DYN_OPT_F_DYNNDX) != 0);
580 	Word	arg1, tmp_val;
581 	Xword	arg2;
582 	int	arg2_known = 1;
583 
584 
585 	elfedit_dyn_elt_init(&strpad_elt);
586 
587 	/*
588 	 * The first argument is an index if -dynndx is used, and is a
589 	 * tag value otherwise.
590 	 */
591 	arg1 = minus_dynndx ?
592 	    elfedit_atoui_range(argstate->argv[0], MSG_ORIG(MSG_STR_ELT),
593 	    0, numdyn - 1, NULL) :
594 	    elfedit_atoconst(argstate->argv[0], ELFEDIT_CONST_DT);
595 
596 	if (minus_s) {
597 		/*
598 		 * Don't allow the user to specify -s when manipulating a
599 		 * DT_SUNW_STRPAD element. Since DT_SUNW_STRPAD is used to
600 		 * manage the extra space used for strings, this would break
601 		 * our ability to add the string.
602 		 */
603 		if ((!minus_dynndx && (arg1 == DT_SUNW_STRPAD)) ||
604 		    (minus_dynndx && (dyn[arg1].d_tag == DT_SUNW_STRPAD)))
605 			elfedit_msg(ELFEDIT_MSG_ERR,
606 			    MSG_INTL(MSG_ERR_STRPADSTRVAL),
607 			    EC_WORD(dynsec->sec_shndx), dynsec->sec_name);
608 
609 		/* Locate DT_SUNW_STRPAD element if present */
610 		strpad_elt.dn_dyn.d_un.d_val = 0;
611 		(void) elfedit_dynstr_getpad(argstate->dyn.sec, &strpad_elt);
612 
613 		/*
614 		 * Look up the string: If the user specified the -dynndx
615 		 * -option, then we will insert it if possible, and
616 		 * fail with an error if not. However, if they did not
617 		 * specify -dynndx, we want to look up the string if it is
618 		 * already there, but defer the insertion. The reason for
619 		 * this is that we may have to grab an unused DT_NULL element
620 		 * below, and if there are none available, we won't want
621 		 * to have modified the string table.
622 		 *
623 		 * This isn't a problem, because if the string isn't
624 		 * in the string table, it can't be used by a dynamic element.
625 		 * Hence, we don't need to insert it to know that there is
626 		 * no match.
627 		 */
628 		if (minus_dynndx == 0) {
629 			if (elfedit_sec_findstr(strsec,
630 			    strpad_elt.dn_dyn.d_un.d_val, argstate->argv[1],
631 			    &tmp_val) == 0) {
632 				arg2_known = 0;
633 			} else {
634 				arg2 = tmp_val;
635 			}
636 		} else {
637 			arg2 = elfedit_dynstr_insert(dynsec, strsec,
638 			    &strpad_elt, argstate->argv[1]);
639 		}
640 	} else {		/* Argument 2 is an integer */
641 		arg2 = elfedit_atoui(argstate->argv[1], NULL);
642 	}
643 
644 
645 	if (!minus_dynndx && !(minus_add && !arg2_known)) {
646 		/*
647 		 * Search the dynamic section and see if an item with the
648 		 * specified tag value already exists. We can reduce this
649 		 * to a simple update of an existing value if -add is not
650 		 * specified or the existing d_un value matches the new one.
651 		 *
652 		 * In either of these cases, we will change arg1 to be the
653 		 * index, and set minus_dynndx, causing the simple update to
654 		 * happen immediately below.
655 		 */
656 		for (i = 0; i < numdyn; i++) {
657 			if ((dyn[i].d_tag == arg1) &&
658 			    (!minus_add || (dyn[i].d_un.d_val == arg2))) {
659 				arg1 = i;
660 				minus_dynndx = 1;
661 				break;
662 			}
663 		}
664 	}
665 
666 	/*
667 	 * If -dynndx is used, then this is a relatively simple
668 	 * operation, as we simply write over the specified index.
669 	 */
670 	if (minus_dynndx) {
671 		/*
672 		 * If we held back from inserting a new string into
673 		 * the dynstr above, we insert it now, because we
674 		 * have a slot in the dynamic section, and we need
675 		 * the string offset ot finish.
676 		 */
677 		if (!arg2_known)
678 			arg2 = elfedit_dynstr_insert(dynsec, strsec,
679 			    &strpad_elt, argstate->argv[1]);
680 
681 		*ret_ndx = arg1;
682 		if (dyn[arg1].d_un.d_val == arg2) {
683 			elfedit_msg(ELFEDIT_MSG_DEBUG,
684 			    MSG_INTL(MSG_DEBUG_X_OK),
685 			    dynsec->sec_shndx, dynsec->sec_name,
686 			    EC_WORD(arg1), EC_XWORD(arg2));
687 			return (ELFEDIT_CMDRET_NONE);
688 		} else {
689 			/* Warn if setting DT_NULL value to non-zero */
690 			if ((dyn[arg1].d_tag == DT_NULL) && (arg2 != 0))
691 				elfedit_msg(ELFEDIT_MSG_DEBUG,
692 				    MSG_INTL(MSG_DEBUG_DTNULLVALUE),
693 				    dynsec->sec_shndx, dynsec->sec_name,
694 				    EC_WORD(arg1), EC_XWORD(arg2));
695 
696 			elfedit_msg(ELFEDIT_MSG_DEBUG,
697 			    MSG_INTL(MSG_DEBUG_X_CHG),
698 			    dynsec->sec_shndx, dynsec->sec_name,
699 			    EC_WORD(arg1), EC_XWORD(dyn[arg1].d_un.d_val),
700 			    EC_XWORD(arg2));
701 			dyn[arg1].d_un.d_val = arg2;
702 			return (ELFEDIT_CMDRET_MOD);
703 		}
704 	}
705 
706 	/*
707 	 * We need a new slot in the dynamic section. If we can't have
708 	 * one, then we fail.
709 	 */
710 	if (argstate->dyn.num_null_ndx <= 1)
711 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOEXTRANULL),
712 		    EC_WORD(dynsec->sec_shndx), dynsec->sec_name);
713 
714 	/*
715 	 * If we still need to insert a new string into the dynstr,
716 	 * then it is safe now, because if we succeed, we know that
717 	 * there is an available slot to receive it. If we fail, we
718 	 * haven't claimed the extra slot yet, and it will be unharmed.
719 	 */
720 	if (!arg2_known)
721 		arg2 = elfedit_dynstr_insert(dynsec, strsec,
722 		    &strpad_elt, argstate->argv[1]);
723 
724 	/* Use an extra DT_NULL slot and enter the new element */
725 	*ret_ndx = convert_dt_null(argstate, arg1, arg2);
726 	return (ELFEDIT_CMDRET_MOD);
727 }
728 
729 
730 
731 /*
732  * Called by cmd_body() for dyn:runpath. Implements the core functionality
733  * for that command.
734  *
735  * History Lesson And Strategy:
736  *
737  * This routine handles both DT_RPATH and DT_RUNPATH entries, altering
738  * either or both if they are present.
739  *
740  * The original SYSV ABI only had DT_RPATH, and the runtime loader used
741  * it to search for things in the following order:
742  *
743  *	DT_RPATH, LD_LIBRARY_PATH, defaults
744  *
745  * Solaris did not follow this rule, an extremely rare deviation from
746  * the ABI. Environment variables should supercede everything else,
747  * otherwise they are not very useful. This decision was made at the
748  * very beginning of the SunOS 5.x development, so we have always
749  * deviated from the ABI and and instead search in the order
750  *
751  *	LD_LIBRARY_PATH, DT_RPATH, defaults
752  *
753  * Other Unix variants initially followed the ABI, but in recent years
754  * have come to agree with the early Solaris folks that it was a mistake.
755  * Hence, DT_RUNPATH was invented, with the search order:
756  *
757  *	LD_LIBRARY_PATH, DT_RUNPATH, defaults
758  *
759  * So for Solaris, DT_RPATH and DT_RUNPATH mean the same thing. If both
760  * are present (which does happen), we set them both to the new
761  * value. If either one is present, we set that one. If neither is
762  * present, and we have a spare DT_NULL slot, we create a DT_RUNPATH, but
763  * not a DT_RPATH, to conserve available slots for other uses.
764  */
765 static elfedit_cmdret_t
766 cmd_body_runpath(ARGSTATE *argstate)
767 {
768 	elfedit_section_t	*dynsec = argstate->dyn.sec;
769 	elfedit_section_t	*strsec = argstate->strsec;
770 	elfedit_dyn_elt_t	rpath_elt;
771 	elfedit_dyn_elt_t	runpath_elt;
772 	elfedit_dyn_elt_t	strpad_elt;
773 	Word			i;
774 	Dyn			*dyn = argstate->dyn.data;
775 	Word			numdyn = argstate->dyn.num;
776 
777 	/* Go through the tags and gather what we need */
778 	elfedit_dyn_elt_init(&rpath_elt);
779 	elfedit_dyn_elt_init(&runpath_elt);
780 	elfedit_dyn_elt_init(&strpad_elt);
781 	for (i = 0; i < numdyn; i++) {
782 		switch (dyn[i].d_tag) {
783 		case DT_RPATH:
784 			elfedit_dyn_elt_save(&rpath_elt, i, &dyn[i]);
785 			break;
786 
787 		case DT_RUNPATH:
788 			elfedit_dyn_elt_save(&runpath_elt, i, &dyn[i]);
789 			break;
790 
791 		case DT_SUNW_STRPAD:
792 			elfedit_dyn_elt_save(&strpad_elt, i, &dyn[i]);
793 			break;
794 		}
795 	}
796 
797 	/*  Do we have an available dynamic section entry to use? */
798 	if (rpath_elt.dn_seen || runpath_elt.dn_seen) {
799 		/*
800 		 * We have seen a DT_RPATH, or a DT_RUNPATH, or both.
801 		 * If all of these have the same string as the desired
802 		 * new value, then we don't need to alter anything and can
803 		 * simply return. Otherwise, we'll modify them all to have
804 		 * the new string (below).
805 		 */
806 		if ((!rpath_elt.dn_seen ||
807 		    (strcmp(elfedit_dyn_offset_to_str(strsec, &rpath_elt),
808 		    argstate->argv[0]) == 0)) &&
809 		    (!runpath_elt.dn_seen ||
810 		    (strcmp(elfedit_dyn_offset_to_str(strsec, &runpath_elt),
811 		    argstate->argv[0]) == 0))) {
812 			if (rpath_elt.dn_seen)
813 				elfedit_msg(ELFEDIT_MSG_DEBUG,
814 				    MSG_INTL(MSG_DEBUG_OLDRPATHOK),
815 				    EC_WORD(dynsec->sec_shndx),
816 				    dynsec->sec_name, EC_WORD(rpath_elt.dn_ndx),
817 				    elfedit_atoconst_value_to_str(
818 				    ELFEDIT_CONST_DT, DT_RPATH, 1));
819 			if (runpath_elt.dn_seen)
820 				elfedit_msg(ELFEDIT_MSG_DEBUG,
821 				    MSG_INTL(MSG_DEBUG_OLDRPATHOK),
822 				    EC_WORD(dynsec->sec_shndx),
823 				    dynsec->sec_name,
824 				    EC_WORD(runpath_elt.dn_ndx),
825 				    elfedit_atoconst_value_to_str(
826 				    ELFEDIT_CONST_DT, DT_RUNPATH, 1));
827 			return (ELFEDIT_CMDRET_NONE);
828 		}
829 	} else if (argstate->dyn.num_null_ndx <= 1) {
830 		/*
831 		 * There is no DT_RPATH or DT_RUNPATH in the dynamic array,
832 		 * and there are no extra DT_NULL entries that we can
833 		 * convert into one. We cannot proceed.
834 		 */
835 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOEXTRANULL),
836 		    EC_WORD(dynsec->sec_shndx), dynsec->sec_name);
837 	}
838 
839 	/* Does the string exist in the table already, or can we add it? */
840 	rpath_elt.dn_dyn.d_un.d_val = runpath_elt.dn_dyn.d_un.d_val =
841 	    elfedit_dynstr_insert(dynsec, strsec, &strpad_elt,
842 	    argstate->argv[0]);
843 
844 	/* Update DT_RPATH entry if present */
845 	if (rpath_elt.dn_seen) {
846 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_PREVRPATH),
847 		    EC_WORD(dynsec->sec_shndx), dynsec->sec_name,
848 		    EC_WORD(rpath_elt.dn_ndx),
849 		    elfedit_atoconst_value_to_str(
850 		    ELFEDIT_CONST_DT, DT_RPATH, 1),
851 		    elfedit_dyn_offset_to_str(strsec, &rpath_elt));
852 		dyn[rpath_elt.dn_ndx] = rpath_elt.dn_dyn;
853 	}
854 
855 	/*
856 	 * Update the DT_RUNPATH entry in the dynamic section, if present.
857 	 * If one is not present, and there is also no DT_RPATH, then
858 	 * we use a spare DT_NULL entry to create a new DT_RUNPATH.
859 	 */
860 	if (runpath_elt.dn_seen || !rpath_elt.dn_seen) {
861 		if (runpath_elt.dn_seen) {
862 			elfedit_msg(ELFEDIT_MSG_DEBUG,
863 			    MSG_INTL(MSG_DEBUG_PREVRPATH),
864 			    EC_WORD(dynsec->sec_shndx), dynsec->sec_name,
865 			    EC_WORD(runpath_elt.dn_ndx),
866 			    elfedit_atoconst_value_to_str(
867 			    ELFEDIT_CONST_DT, DT_RUNPATH, 1),
868 			    elfedit_dyn_offset_to_str(strsec, &runpath_elt));
869 			dyn[runpath_elt.dn_ndx] = runpath_elt.dn_dyn;
870 		} else {	/* Using a spare DT_NULL entry */
871 			(void) convert_dt_null(argstate, DT_RUNPATH,
872 			    runpath_elt.dn_dyn.d_un.d_val);
873 		}
874 	}
875 
876 	return (ELFEDIT_CMDRET_MOD);
877 }
878 
879 
880 
881 /*
882  * Argument processing for the bitmask commands. Convert the arguments
883  * to integer form, apply -and/-cmp/-or, and return the resulting value.
884  *
885  * entry:
886  *	argstate - Argument state block
887  *	orig - Value of original bitmask
888  *	const_type - ELFEDIT_CONST_* value for type of constants
889  */
890 static Word
891 flag_bitop(ARGSTATE *argstate, Word orig, elfedit_const_t const_type)
892 {
893 	Word flags = 0;
894 	int i;
895 
896 	/* Collect the arguments */
897 	for (i = 0; i < argstate->argc; i++)
898 		flags |= (Word) elfedit_atoconst(argstate->argv[i], const_type);
899 
900 	/* Complement the value? */
901 	if (argstate->optmask & DYN_OPT_F_CMP)
902 		flags = ~flags;
903 
904 	/* Perform any requested bit operations */
905 	if (argstate->optmask & DYN_OPT_F_AND)
906 		flags &= orig;
907 	else if (argstate->optmask & DYN_OPT_F_OR)
908 		flags |= orig;
909 
910 	return (flags);
911 }
912 
913 
914 
915 /*
916  * Common body for the dyn: module commands. These commands
917  * share a large amount of common behavior, so it is convenient
918  * to centralize things and use the cmd argument to handle the
919  * small differences.
920  *
921  * entry:
922  *	cmd - One of the DYN_CMD_T_* constants listed above, specifying
923  *		which command to implement.
924  *	obj_state, argc, argv - Standard command arguments
925  */
926 static elfedit_cmdret_t
927 cmd_body(DYN_CMD_T cmd, elfedit_obj_state_t *obj_state,
928     int argc, const char *argv[])
929 {
930 	ARGSTATE		argstate;
931 	Dyn			*dyn;
932 	const char		*dyn_name;
933 	Word			dyn_ndx, dyn_num, null_ndx;
934 	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
935 	PRINT_DYN_T		print_type = PRINT_DYN_T_ALL;
936 	Word			ndx;
937 	int			print_only = 0;
938 	int			do_autoprint = 1;
939 
940 	/* Process the optional arguments */
941 	process_args(obj_state, argc, argv, &argstate);
942 
943 	dyn = argstate.dyn.data;
944 	dyn_num = argstate.dyn.num;
945 	dyn_name = argstate.dyn.sec->sec_name;
946 	dyn_ndx = argstate.dyn.sec->sec_shndx;
947 
948 	/* Check number of arguments, gather information */
949 	switch (cmd) {
950 	case DYN_CMD_T_DUMP:
951 		/* dyn:dump can accept an optional index argument */
952 		if (argstate.argc > 1)
953 			elfedit_command_usage();
954 		print_only = 1;
955 		if (argstate.argc == 1)
956 			ndx = arg_to_index(&argstate, argstate.argv[0],
957 			    MSG_ORIG(MSG_STR_ELT), print_only, &print_type);
958 		break;
959 
960 	case DYN_CMD_T_TAG:
961 		print_only = (argstate.argc != 2);
962 		if (argstate.argc > 0) {
963 			if (argstate.argc > 2)
964 				elfedit_command_usage();
965 			ndx = arg_to_index(&argstate, argstate.argv[0],
966 			    MSG_ORIG(MSG_STR_ELT), print_only, &print_type);
967 		}
968 		break;
969 
970 	case DYN_CMD_T_VALUE:
971 		print_only = (argstate.argc != 2);
972 		if (argstate.argc > 2)
973 			elfedit_command_usage();
974 		if (argstate.argc > 0) {
975 			if (print_only) {
976 				ndx = arg_to_index(&argstate, argstate.argv[0],
977 				    MSG_ORIG(MSG_STR_ELT),
978 				    print_only, &print_type);
979 			} else {
980 				print_type = PRINT_DYN_T_NDX;
981 			}
982 		}
983 		break;
984 
985 	case DYN_CMD_T_DELETE:
986 		if ((argstate.argc < 1) || (argstate.argc > 2))
987 			elfedit_command_usage();
988 		ndx = arg_to_index(&argstate, argstate.argv[0],
989 		    MSG_ORIG(MSG_STR_ELT),
990 		    0, &print_type);
991 		do_autoprint = 0;
992 		break;
993 
994 	case DYN_CMD_T_MOVE:
995 		if ((argstate.argc < 2) || (argstate.argc > 3))
996 			elfedit_command_usage();
997 		ndx = arg_to_index(&argstate, argstate.argv[0],
998 		    MSG_ORIG(MSG_STR_ELT), 0, &print_type);
999 		do_autoprint = 0;
1000 		break;
1001 
1002 	case DYN_CMD_T_RUNPATH:
1003 		if (argstate.argc > 1)
1004 			elfedit_command_usage();
1005 		/*
1006 		 * dyn:runpath does not accept an explicit index
1007 		 * argument, so we implicitly only show the DT_RPATH and
1008 		 * DT_RUNPATH elements.
1009 		 */
1010 		print_type = PRINT_DYN_T_RUNPATH;
1011 		print_only = (argstate.argc == 0);
1012 		break;
1013 
1014 	case DYN_CMD_T_POSFLAG1:
1015 		print_only = (argstate.argc == 0);
1016 		ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str(
1017 		    ELFEDIT_CONST_DT, DT_POSFLAG_1, 1),
1018 		    MSG_ORIG(MSG_STR_VALUE), print_only, &print_type);
1019 		break;
1020 
1021 	case DYN_CMD_T_FLAGS:
1022 		print_only = (argstate.argc == 0);
1023 		ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str(
1024 		    ELFEDIT_CONST_DT, DT_FLAGS, 1),
1025 		    MSG_ORIG(MSG_STR_VALUE), print_only, &print_type);
1026 		break;
1027 
1028 	case DYN_CMD_T_FLAGS1:
1029 		print_only = (argstate.argc == 0);
1030 		ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str(
1031 		    ELFEDIT_CONST_DT, DT_FLAGS_1, 1),
1032 		    MSG_ORIG(MSG_STR_VALUE), print_only, &print_type);
1033 		break;
1034 
1035 	case DYN_CMD_T_FEATURE1:
1036 		print_only = (argstate.argc == 0);
1037 		ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str(
1038 		    ELFEDIT_CONST_DT, DT_FEATURE_1, 1),
1039 		    MSG_ORIG(MSG_STR_VALUE), print_only, &print_type);
1040 		break;
1041 
1042 	case DYN_CMD_T_CHECKSUM:
1043 		ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str(
1044 		    ELFEDIT_CONST_DT, DT_CHECKSUM, 1),
1045 		    MSG_ORIG(MSG_STR_VALUE), print_only, &print_type);
1046 		break;
1047 
1048 	default:
1049 		/* Note expected: All commands should have been caught above */
1050 		elfedit_command_usage();
1051 		break;
1052 	}
1053 
1054 
1055 	/* If this is a request to print current values, do it and return */
1056 	if (print_only) {
1057 		print_dyn(cmd, 0, &argstate, print_type, ndx);
1058 		return (ELFEDIT_CMDRET_NONE);
1059 	}
1060 
1061 
1062 	switch (cmd) {
1063 		/*
1064 		 * DYN_CMD_T_DUMP can't get here: It is a print-only
1065 		 * command.
1066 		 */
1067 
1068 	case DYN_CMD_T_TAG:
1069 		{
1070 			Conv_inv_buf_t	inv_buf1, inv_buf2;
1071 			Half	mach = argstate.obj_state->os_ehdr->e_machine;
1072 			Word d_tag = (Word) elfedit_atoconst(argstate.argv[1],
1073 			    ELFEDIT_CONST_DT);
1074 
1075 			if (dyn[ndx].d_tag == d_tag) {
1076 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1077 				    MSG_INTL(MSG_DEBUG_S_OK),
1078 				    dyn_ndx,
1079 				    dyn_name, EC_WORD(ndx),
1080 				    conv_dyn_tag(d_tag, mach, 0, &inv_buf1));
1081 			} else {
1082 				Word orig_d_tag = dyn[ndx].d_tag;
1083 
1084 				ret = ELFEDIT_CMDRET_MOD;
1085 				dyn[ndx].d_tag = d_tag;
1086 
1087 				/*
1088 				 * Update null termination index. Warn if we
1089 				 * just clobbered the only DT_NULL termination
1090 				 * for the array.
1091 				 */
1092 				null_ndx = argstate.dyn.null_ndx;
1093 				set_null_ndx(&argstate);
1094 				if ((argstate.dyn.null_ndx >=
1095 				    argstate.dyn.num) &&
1096 				    (null_ndx != argstate.dyn.null_ndx))
1097 					elfedit_msg(ELFEDIT_MSG_DEBUG,
1098 					    MSG_INTL(MSG_DEBUG_NULLTERM),
1099 					    dyn_ndx, dyn_name,
1100 					    EC_WORD(ndx),
1101 					    conv_dyn_tag(d_tag, mach,
1102 					    0, &inv_buf1));
1103 
1104 				/*
1105 				 * Warning if
1106 				 *	- Inserting a DT_NULL cuts off following
1107 				 *		non-null elements.
1108 				 *	- Inserting a non-DT_NULL after the
1109 				 *		first null element, will be
1110 				 *		ignored by rtld.
1111 				 */
1112 				if (d_tag == DT_NULL) {
1113 					if ((ndx + 1) < null_ndx)
1114 						elfedit_msg(ELFEDIT_MSG_DEBUG,
1115 						    MSG_INTL(MSG_DEBUG_NULCLIP),
1116 						    dyn_ndx, dyn_name,
1117 						    EC_WORD(ndx),
1118 						    conv_dyn_tag(d_tag, mach,
1119 						    0, &inv_buf1));
1120 				} else {
1121 					if ((ndx + 1) > argstate.dyn.null_ndx)
1122 						elfedit_msg(ELFEDIT_MSG_DEBUG,
1123 						    MSG_INTL(MSG_DEBUG_NULHIDE),
1124 						    dyn_ndx, dyn_name,
1125 						    EC_WORD(ndx),
1126 						    conv_dyn_tag(d_tag, mach,
1127 						    0, &inv_buf1));
1128 				}
1129 
1130 				/* Debug message that we changed it */
1131 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1132 				    MSG_INTL(MSG_DEBUG_S_CHG),
1133 				    dyn_ndx, dyn_name, EC_WORD(ndx),
1134 				    conv_dyn_tag(orig_d_tag, mach, 0,
1135 				    &inv_buf1),
1136 				    conv_dyn_tag(d_tag, mach, 0, &inv_buf2));
1137 			}
1138 		}
1139 		break;
1140 
1141 	case DYN_CMD_T_VALUE:
1142 		ret = cmd_body_value(&argstate, &ndx);
1143 		break;
1144 
1145 	case DYN_CMD_T_DELETE:
1146 		{
1147 			Word cnt = (argstate.argc == 1) ? 1 :
1148 			    (Word) elfedit_atoui_range(argstate.argv[1],
1149 			    MSG_ORIG(MSG_STR_COUNT), 1, dyn_num - ndx, NULL);
1150 			const char *msg_prefix =
1151 			    elfedit_sec_msgprefix(argstate.dyn.sec);
1152 
1153 			elfedit_array_elts_delete(msg_prefix, argstate.dyn.data,
1154 			    sizeof (Dyn), dyn_num, ndx, cnt);
1155 			ret = ELFEDIT_CMDRET_MOD;
1156 		}
1157 		break;
1158 
1159 	case DYN_CMD_T_MOVE:
1160 		{
1161 			Dyn	save;
1162 			Word	cnt;
1163 			Word	dstndx;
1164 			const char *msg_prefix =
1165 			    elfedit_sec_msgprefix(argstate.dyn.sec);
1166 
1167 			dstndx = (Word)
1168 			    elfedit_atoui_range(argstate.argv[1],
1169 			    MSG_ORIG(MSG_STR_DST_INDEX), 0, dyn_num - 1,
1170 			    NULL);
1171 			if (argstate.argc == 2) {
1172 				cnt = 1;
1173 			} else {
1174 				cnt = (Word) elfedit_atoui_range(
1175 				    argstate.argv[2], MSG_ORIG(MSG_STR_COUNT),
1176 				    1, dyn_num, NULL);
1177 			}
1178 			elfedit_array_elts_move(msg_prefix, argstate.dyn.data,
1179 			    sizeof (save), dyn_num, ndx, dstndx, cnt, &save);
1180 			ret = ELFEDIT_CMDRET_MOD;
1181 		}
1182 		break;
1183 
1184 
1185 	case DYN_CMD_T_RUNPATH:
1186 		ret = cmd_body_runpath(&argstate);
1187 		break;
1188 
1189 	case DYN_CMD_T_POSFLAG1:
1190 		{
1191 			Conv_dyn_posflag1_buf_t buf1, buf2;
1192 			Word flags;
1193 
1194 			flags = flag_bitop(&argstate, dyn[ndx].d_un.d_val,
1195 			    ELFEDIT_CONST_DF_P1);
1196 
1197 			/* Set the value */
1198 			if (dyn[ndx].d_un.d_val == flags) {
1199 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1200 				    MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx,
1201 				    dyn_name, EC_WORD(ndx),
1202 				    conv_dyn_posflag1(dyn[ndx].d_un.d_val, 0,
1203 				    &buf1));
1204 			} else {
1205 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1206 				    MSG_INTL(MSG_DEBUG_S_CHG),
1207 				    dyn_ndx, dyn_name, EC_WORD(ndx),
1208 				    conv_dyn_posflag1(dyn[ndx].d_un.d_val, 0,
1209 				    &buf1),
1210 				    conv_dyn_posflag1(flags, 0, &buf2));
1211 				ret = ELFEDIT_CMDRET_MOD;
1212 				dyn[ndx].d_un.d_val = flags;
1213 			}
1214 		}
1215 		break;
1216 
1217 	case DYN_CMD_T_FLAGS:
1218 		{
1219 			Conv_dyn_flag_buf_t buf1, buf2;
1220 			Word flags;
1221 
1222 			flags = flag_bitop(&argstate, dyn[ndx].d_un.d_val,
1223 			    ELFEDIT_CONST_DF);
1224 
1225 			/* Set the value */
1226 			if (dyn[ndx].d_un.d_val == flags) {
1227 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1228 				    MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx,
1229 				    dyn_name, EC_WORD(ndx),
1230 				    conv_dyn_flag(dyn[ndx].d_un.d_val, 0,
1231 				    &buf1));
1232 			} else {
1233 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1234 				    MSG_INTL(MSG_DEBUG_S_CHG),
1235 				    dyn_ndx, dyn_name, EC_WORD(ndx),
1236 				    conv_dyn_flag(dyn[ndx].d_un.d_val, 0,
1237 				    &buf1),
1238 				    conv_dyn_flag(flags, 0, &buf2));
1239 				ret = ELFEDIT_CMDRET_MOD;
1240 				dyn[ndx].d_un.d_val = flags;
1241 			}
1242 		}
1243 		break;
1244 
1245 	case DYN_CMD_T_FLAGS1:
1246 		{
1247 			Conv_dyn_flag1_buf_t buf1, buf2;
1248 			Word flags1;
1249 
1250 			flags1 = flag_bitop(&argstate, dyn[ndx].d_un.d_val,
1251 			    ELFEDIT_CONST_DF_1);
1252 
1253 			/* Set the value */
1254 			if (dyn[ndx].d_un.d_val == flags1) {
1255 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1256 				    MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx,
1257 				    dyn_name, EC_WORD(ndx),
1258 				    conv_dyn_flag1(dyn[ndx].d_un.d_val,
1259 				    0, &buf1));
1260 			} else {
1261 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1262 				    MSG_INTL(MSG_DEBUG_S_CHG),
1263 				    dyn_ndx, dyn_name, EC_WORD(ndx),
1264 				    conv_dyn_flag1(dyn[ndx].d_un.d_val,
1265 				    0, &buf1),
1266 				    conv_dyn_flag1(flags1, 0, &buf2));
1267 				ret = ELFEDIT_CMDRET_MOD;
1268 				dyn[ndx].d_un.d_val = flags1;
1269 			}
1270 		}
1271 		break;
1272 
1273 	case DYN_CMD_T_FEATURE1:
1274 		{
1275 			Conv_dyn_feature1_buf_t buf1, buf2;
1276 			Word flags;
1277 
1278 			flags = flag_bitop(&argstate, dyn[ndx].d_un.d_val,
1279 			    ELFEDIT_CONST_DTF_1);
1280 
1281 			/* Set the value */
1282 			if (dyn[ndx].d_un.d_val == flags) {
1283 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1284 				    MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx,
1285 				    dyn_name, EC_WORD(ndx),
1286 				    conv_dyn_feature1(dyn[ndx].d_un.d_val, 0,
1287 				    &buf1));
1288 			} else {
1289 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1290 				    MSG_INTL(MSG_DEBUG_S_CHG),
1291 				    dyn_ndx, dyn_name, EC_WORD(ndx),
1292 				    conv_dyn_feature1(dyn[ndx].d_un.d_val, 0,
1293 				    &buf1),
1294 				    conv_dyn_feature1(flags, 0, &buf2));
1295 				ret = ELFEDIT_CMDRET_MOD;
1296 				dyn[ndx].d_un.d_val = flags;
1297 			}
1298 		}
1299 		break;
1300 
1301 	case DYN_CMD_T_CHECKSUM:
1302 		{
1303 			long checksum = elf_checksum(obj_state->os_elf);
1304 
1305 			/* Set the value */
1306 			if (dyn[ndx].d_un.d_val == checksum) {
1307 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1308 				    MSG_INTL(MSG_DEBUG_X_OK), dyn_ndx,
1309 				    dyn_name, EC_WORD(ndx), EC_XWORD(checksum));
1310 			} else {
1311 				elfedit_msg(ELFEDIT_MSG_DEBUG,
1312 				    MSG_INTL(MSG_DEBUG_X_CHG),
1313 				    dyn_ndx, dyn_name, EC_WORD(ndx),
1314 				    EC_XWORD(dyn[ndx].d_un.d_val),
1315 				    EC_XWORD(checksum));
1316 				ret = ELFEDIT_CMDRET_MOD;
1317 				dyn[ndx].d_un.d_val = checksum;
1318 			}
1319 
1320 		}
1321 	}
1322 
1323 	/*
1324 	 * If we modified the dynamic section header, tell libelf.
1325 	 */
1326 	if (ret == ELFEDIT_CMDRET_MOD)
1327 		elfedit_modified_data(argstate.dyn.sec);
1328 
1329 	/* Do autoprint */
1330 	if (do_autoprint)
1331 		print_dyn(cmd, 1, &argstate, print_type, ndx);
1332 
1333 	return (ret);
1334 }
1335 
1336 
1337 
1338 /*
1339  * Command completion functions for the commands
1340  */
1341 
1342 /*
1343  * Command completion for the first argument, which specifies
1344  * the dynamic element to use. Examines the options to see if
1345  * -dynndx is present, and if not, supplies the completion
1346  * strings for argument 1.
1347  */
1348 /*ARGSUSED*/
1349 static void
1350 cpl_eltarg(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
1351     const char *argv[], int num_opt)
1352 {
1353 	elfedit_section_t	*cache;
1354 	Dyn			*dyn;
1355 	Word			i;
1356 	const char		*s;
1357 	char			*s2;
1358 	char			buf[128];
1359 
1360 	/* Make sure it's the first argument */
1361 	if ((argc - num_opt) != 1)
1362 		return;
1363 
1364 	/* Is -dynndx present? If so, we don't complete tag types */
1365 	for (i = 0; i < num_opt; i++)
1366 		if (strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_DYNNDX)) == 0)
1367 			return;
1368 
1369 	/*
1370 	 * If there is no object, or if there is no dynamic section,
1371 	 * then supply all possible names.
1372 	 */
1373 	if ((obj_state == NULL) || (obj_state->os_dynndx == SHN_UNDEF)) {
1374 		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DT);
1375 		return;
1376 	}
1377 
1378 	/* Supply completions for the tags present in the dynamic section */
1379 	cache = &obj_state->os_secarr[obj_state->os_dynndx];
1380 	dyn = (Dyn *) cache->sec_data->d_buf;
1381 	i = cache->sec_shdr->sh_size / cache->sec_shdr->sh_entsize;
1382 	for (; i-- > 0; dyn++) {
1383 		s = elfedit_atoconst_value_to_str(ELFEDIT_CONST_DT,
1384 		    dyn->d_tag, 0);
1385 		if (s == NULL)
1386 			continue;
1387 		elfedit_cpl_match(cpldata, s, 1);
1388 
1389 		/*
1390 		 * To get the informal tag names that are lowercase
1391 		 * and lack the leading DT_, we copy the string we
1392 		 * have into a buffer and process it.
1393 		 */
1394 		if (strlen(s) < 3)
1395 			continue;
1396 		(void) strlcpy(buf, s + 3, sizeof (buf));
1397 		for (s2 = buf; *s2 != '\0'; s2++)
1398 			if (isupper(*s2))
1399 				*s2 = tolower(*s2);
1400 		elfedit_cpl_match(cpldata, buf, 1);
1401 	}
1402 }
1403 
1404 
1405 /*ARGSUSED*/
1406 static void
1407 cpl_tag(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
1408     const char *argv[], int num_opt)
1409 {
1410 	/* First argument */
1411 	if ((argc - num_opt) == 1) {
1412 		cpl_eltarg(obj_state, cpldata, argc, argv, num_opt);
1413 		return;
1414 	}
1415 
1416 	/* The second argument is always a tag value */
1417 	if ((argc - num_opt) == 2)
1418 		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DT);
1419 }
1420 
1421 /*ARGSUSED*/
1422 static void
1423 cpl_posflag1(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
1424     const char *argv[], int num_opt)
1425 {
1426 	/* This routine allows multiple flags to be specified */
1427 	elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DF_P1);
1428 }
1429 
1430 /*ARGSUSED*/
1431 static void
1432 cpl_flags(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
1433     const char *argv[], int num_opt)
1434 {
1435 	/* This routine allows multiple flags to be specified */
1436 	elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DF);
1437 }
1438 
1439 /*ARGSUSED*/
1440 static void
1441 cpl_flags1(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
1442     const char *argv[], int num_opt)
1443 {
1444 	/* This routine allows multiple flags to be specified */
1445 	elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DF_1);
1446 }
1447 
1448 /*ARGSUSED*/
1449 static void
1450 cpl_feature1(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
1451     const char *argv[], int num_opt)
1452 {
1453 	/* This routine allows multiple flags to be specified */
1454 	elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DTF_1);
1455 }
1456 
1457 
1458 /*
1459  * Implementation functions for the commands
1460  */
1461 static elfedit_cmdret_t
1462 cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1463 {
1464 	return (cmd_body(DYN_CMD_T_DUMP, obj_state, argc, argv));
1465 }
1466 
1467 static elfedit_cmdret_t
1468 cmd_tag(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1469 {
1470 	return (cmd_body(DYN_CMD_T_TAG, obj_state, argc, argv));
1471 }
1472 
1473 static elfedit_cmdret_t
1474 cmd_value(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1475 {
1476 	return (cmd_body(DYN_CMD_T_VALUE, obj_state, argc, argv));
1477 }
1478 
1479 static elfedit_cmdret_t
1480 cmd_delete(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1481 {
1482 	return (cmd_body(DYN_CMD_T_DELETE, obj_state, argc, argv));
1483 }
1484 
1485 static elfedit_cmdret_t
1486 cmd_move(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1487 {
1488 	return (cmd_body(DYN_CMD_T_MOVE, obj_state, argc, argv));
1489 }
1490 
1491 static elfedit_cmdret_t
1492 cmd_runpath(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1493 {
1494 	return (cmd_body(DYN_CMD_T_RUNPATH, obj_state, argc, argv));
1495 }
1496 
1497 static elfedit_cmdret_t
1498 cmd_posflag1(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1499 {
1500 	return (cmd_body(DYN_CMD_T_POSFLAG1, obj_state, argc, argv));
1501 }
1502 
1503 static elfedit_cmdret_t
1504 cmd_flags(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1505 {
1506 	return (cmd_body(DYN_CMD_T_FLAGS, obj_state, argc, argv));
1507 }
1508 
1509 static elfedit_cmdret_t
1510 cmd_flags1(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1511 {
1512 	return (cmd_body(DYN_CMD_T_FLAGS1, obj_state, argc, argv));
1513 }
1514 
1515 static elfedit_cmdret_t
1516 cmd_feature1(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1517 {
1518 	return (cmd_body(DYN_CMD_T_FEATURE1, obj_state, argc, argv));
1519 }
1520 
1521 static elfedit_cmdret_t
1522 cmd_checksum(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
1523 {
1524 	return (cmd_body(DYN_CMD_T_CHECKSUM, obj_state, argc, argv));
1525 }
1526 
1527 
1528 
1529 /*ARGSUSED*/
1530 elfedit_module_t *
1531 elfedit_init(elfedit_module_version_t version)
1532 {
1533 	/* For commands that only accept -o */
1534 	static elfedit_cmd_optarg_t opt_ostyle[] = {
1535 		{ ELFEDIT_STDOA_OPT_O, NULL,
1536 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
1537 		{ NULL }
1538 	};
1539 
1540 	/* For commands that only accept -and, -cmp, -o, -or */
1541 	static elfedit_cmd_optarg_t opt_ostyle_bitop[] = {
1542 		{ ELFEDIT_STDOA_OPT_AND, NULL,
1543 		    ELFEDIT_CMDOA_F_INHERIT, DYN_OPT_F_AND, DYN_OPT_F_OR },
1544 		{ ELFEDIT_STDOA_OPT_CMP, NULL,
1545 		    ELFEDIT_CMDOA_F_INHERIT, DYN_OPT_F_CMP, 0 },
1546 		{ ELFEDIT_STDOA_OPT_O, NULL,
1547 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
1548 		{ ELFEDIT_STDOA_OPT_OR, NULL,
1549 		    ELFEDIT_CMDOA_F_INHERIT, DYN_OPT_F_OR, DYN_OPT_F_AND },
1550 		{ NULL }
1551 	};
1552 
1553 	/* For commands that only accept -dynndx */
1554 	static elfedit_cmd_optarg_t opt_minus_dynndx[] = {
1555 		{ MSG_ORIG(MSG_STR_MINUS_DYNNDX),
1556 		    /* MSG_INTL(MSG_OPTDESC_DYNNDX) */
1557 		    ELFEDIT_I18NHDL(MSG_OPTDESC_DYNNDX), 0,
1558 		    DYN_OPT_F_DYNNDX, 0 },
1559 		{ NULL }
1560 	};
1561 
1562 	/* dyn:dump */
1563 	static const char *name_dump[] = {
1564 	    MSG_ORIG(MSG_CMD_DUMP),
1565 	    MSG_ORIG(MSG_STR_EMPTY),	/* "" makes this the default command */
1566 	    NULL
1567 	};
1568 	static elfedit_cmd_optarg_t arg_dump[] = {
1569 		{ MSG_ORIG(MSG_STR_ELT),
1570 		    /* MSG_INTL(MSG_ARGDESC_ELT) */
1571 		    ELFEDIT_I18NHDL(MSG_ARGDESC_ELT),
1572 		    ELFEDIT_CMDOA_F_OPT },
1573 		{ NULL }
1574 	};
1575 
1576 
1577 	/* dyn:tag */
1578 	static const char *name_tag[] = { MSG_ORIG(MSG_CMD_TAG), NULL };
1579 	static elfedit_cmd_optarg_t opt_tag[] = {
1580 		{ MSG_ORIG(MSG_STR_MINUS_DYNNDX),
1581 		    /* MSG_INTL(MSG_OPTDESC_DYNNDX) */
1582 		    ELFEDIT_I18NHDL(MSG_OPTDESC_DYNNDX), 0,
1583 		    DYN_OPT_F_DYNNDX, 0 },
1584 		{ ELFEDIT_STDOA_OPT_O, NULL,
1585 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
1586 		{ NULL }
1587 	};
1588 	static elfedit_cmd_optarg_t arg_tag[] = {
1589 		{ MSG_ORIG(MSG_STR_ELT),
1590 		    /* MSG_INTL(MSG_A1_TAG_ELT) */
1591 		    ELFEDIT_I18NHDL(MSG_A1_TAG_ELT),
1592 		    ELFEDIT_CMDOA_F_OPT },
1593 		{ MSG_ORIG(MSG_STR_VALUE),
1594 		    /* MSG_INTL(MSG_A2_TAG_VALUE) */
1595 		    ELFEDIT_I18NHDL(MSG_A2_TAG_VALUE),
1596 		    ELFEDIT_CMDOA_F_OPT },
1597 		{ NULL }
1598 	};
1599 
1600 
1601 	/* dyn:value */
1602 	static const char *name_value[] = { MSG_ORIG(MSG_CMD_VALUE), NULL };
1603 	static elfedit_cmd_optarg_t opt_value[] = {
1604 		{ MSG_ORIG(MSG_STR_MINUS_ADD),
1605 		    /* MSG_INTL(MSG_OPTDESC_ADD) */
1606 		    ELFEDIT_I18NHDL(MSG_OPTDESC_ADD), 0,
1607 		    DYN_OPT_F_ADD, DYN_OPT_F_DYNNDX },
1608 		{ MSG_ORIG(MSG_STR_MINUS_DYNNDX),
1609 		    /* MSG_INTL(MSG_OPTDESC_DYNNDX) */
1610 		    ELFEDIT_I18NHDL(MSG_OPTDESC_DYNNDX), 0,
1611 		    DYN_OPT_F_DYNNDX, DYN_OPT_F_ADD },
1612 		{ ELFEDIT_STDOA_OPT_O, NULL,
1613 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
1614 		{ MSG_ORIG(MSG_STR_MINUS_S),
1615 		    /* MSG_INTL(MSG_OPTDESC_S) */
1616 		    ELFEDIT_I18NHDL(MSG_OPTDESC_S), 0,
1617 		    DYN_OPT_F_STRVAL, 0 },
1618 		{ NULL }
1619 	};
1620 	static elfedit_cmd_optarg_t arg_value[] = {
1621 		{ MSG_ORIG(MSG_STR_ELT),
1622 		    /* MSG_INTL(MSG_ARGDESC_ELT) */
1623 		    ELFEDIT_I18NHDL(MSG_ARGDESC_ELT),
1624 		    ELFEDIT_CMDOA_F_OPT },
1625 		{ MSG_ORIG(MSG_STR_VALUE),
1626 		    /* MSG_INTL(MSG_A2_VALUE_VALUE) */
1627 		    ELFEDIT_I18NHDL(MSG_A2_VALUE_VALUE),
1628 		    ELFEDIT_CMDOA_F_OPT },
1629 		{ NULL }
1630 	};
1631 
1632 	/* dyn:delete */
1633 	static const char *name_delete[] = { MSG_ORIG(MSG_CMD_DELETE), NULL };
1634 	static elfedit_cmd_optarg_t arg_delete[] = {
1635 		{ MSG_ORIG(MSG_STR_ELT),
1636 		    /* MSG_INTL(MSG_ARGDESC_ELT) */
1637 		    ELFEDIT_I18NHDL(MSG_ARGDESC_ELT),
1638 		    0 },
1639 		{ MSG_ORIG(MSG_STR_COUNT),
1640 		    /* MSG_INTL(MSG_A2_DELETE_COUNT) */
1641 		    ELFEDIT_I18NHDL(MSG_A2_DELETE_COUNT),
1642 		    ELFEDIT_CMDOA_F_OPT },
1643 		{ NULL }
1644 	};
1645 
1646 	/* dyn:move */
1647 	static const char *name_move[] = { MSG_ORIG(MSG_CMD_MOVE), NULL };
1648 	static elfedit_cmd_optarg_t arg_move[] = {
1649 		{ MSG_ORIG(MSG_STR_ELT),
1650 		    /* MSG_INTL(MSG_ARGDESC_ELT) */
1651 		    ELFEDIT_I18NHDL(MSG_ARGDESC_ELT),
1652 		    0 },
1653 		{ MSG_ORIG(MSG_STR_DST_INDEX),
1654 		    /* MSG_INTL(MSG_A2_MOVE_DST_INDEX) */
1655 		    ELFEDIT_I18NHDL(MSG_A2_MOVE_DST_INDEX),
1656 		    0 },
1657 		{ MSG_ORIG(MSG_STR_COUNT),
1658 		    /* MSG_INTL(MSG_A3_MOVE_COUNT) */
1659 		    ELFEDIT_I18NHDL(MSG_A3_MOVE_COUNT),
1660 		    ELFEDIT_CMDOA_F_OPT },
1661 		{ NULL }
1662 	};
1663 
1664 	/* dyn:runpath / dyn:rpath */
1665 	static const char *name_runpath[] = { MSG_ORIG(MSG_CMD_RUNPATH),
1666 	    MSG_ORIG(MSG_CMD_RUNPATH_A1), NULL };
1667 	static elfedit_cmd_optarg_t arg_runpath[] = {
1668 		{ MSG_ORIG(MSG_STR_NEWPATH),
1669 		    /* MSG_INTL(MSG_A1_RUNPATH_NEWPATH) */
1670 		    ELFEDIT_I18NHDL(MSG_A1_RUNPATH_NEWPATH),
1671 		    ELFEDIT_CMDOA_F_OPT },
1672 		{ NULL }
1673 	};
1674 
1675 	/* dyn:posflag1 */
1676 	static const char *name_posflag1[] = { MSG_ORIG(MSG_CMD_POSFLAG1),
1677 	    NULL };
1678 	static elfedit_cmd_optarg_t arg_posflag1[] = {
1679 		{ MSG_ORIG(MSG_STR_VALUE),
1680 		    /* MSG_INTL(MSG_A1_POSFLAG1_VALUE) */
1681 		    ELFEDIT_I18NHDL(MSG_A1_POSFLAG1_VALUE),
1682 		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
1683 		{ NULL }
1684 	};
1685 
1686 	/* dyn:flags */
1687 	static const char *name_flags[] = { MSG_ORIG(MSG_CMD_FLAGS), NULL };
1688 	static elfedit_cmd_optarg_t arg_flags[] = {
1689 		{ MSG_ORIG(MSG_STR_VALUE),
1690 		    /* MSG_INTL(MSG_A1_FLAGS_VALUE) */
1691 		    ELFEDIT_I18NHDL(MSG_A1_FLAGS_VALUE),
1692 		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
1693 		{ NULL }
1694 	};
1695 
1696 	/* dyn:flags1 */
1697 	static const char *name_flags1[] = { MSG_ORIG(MSG_CMD_FLAGS1), NULL };
1698 	static elfedit_cmd_optarg_t arg_flags1[] = {
1699 		{ MSG_ORIG(MSG_STR_VALUE),
1700 		    /* MSG_INTL(MSG_A1_FLAGS1_VALUE) */
1701 		    ELFEDIT_I18NHDL(MSG_A1_FLAGS1_VALUE),
1702 		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
1703 		{ NULL }
1704 	};
1705 
1706 	/* dyn:feature1 */
1707 	static const char *name_feature1[] = { MSG_ORIG(MSG_CMD_FEATURE1),
1708 	    NULL };
1709 	static elfedit_cmd_optarg_t arg_feature1[] = {
1710 		{ MSG_ORIG(MSG_STR_VALUE),
1711 		    /* MSG_INTL(MSG_A1_FEATURE1_VALUE) */
1712 		    ELFEDIT_I18NHDL(MSG_A1_FEATURE1_VALUE),
1713 		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
1714 		{ NULL }
1715 	};
1716 
1717 	/* dyn:checksum */
1718 	static const char *name_checksum[] = { MSG_ORIG(MSG_CMD_CHECKSUM),
1719 	    NULL };
1720 
1721 
1722 
1723 	static elfedit_cmd_t cmds[] = {
1724 		/* dyn:dump */
1725 		{ cmd_dump, cpl_eltarg, name_dump,
1726 		    /* MSG_INTL(MSG_DESC_DUMP) */
1727 		    ELFEDIT_I18NHDL(MSG_DESC_DUMP),
1728 		    /* MSG_INTL(MSG_HELP_DUMP) */
1729 		    ELFEDIT_I18NHDL(MSG_HELP_DUMP),
1730 		    opt_minus_dynndx, arg_dump },
1731 
1732 		/* dyn:tag */
1733 		{ cmd_tag, cpl_tag, name_tag,
1734 		    /* MSG_INTL(MSG_DESC_TAG) */
1735 		    ELFEDIT_I18NHDL(MSG_DESC_TAG),
1736 		    /* MSG_INTL(MSG_HELP_TAG) */
1737 		    ELFEDIT_I18NHDL(MSG_HELP_TAG),
1738 		    opt_tag, arg_tag },
1739 
1740 		/* dyn:value */
1741 		{ cmd_value, cpl_eltarg, name_value,
1742 		    /* MSG_INTL(MSG_DESC_VALUE) */
1743 		    ELFEDIT_I18NHDL(MSG_DESC_VALUE),
1744 		    /* MSG_INTL(MSG_HELP_VALUE) */
1745 		    ELFEDIT_I18NHDL(MSG_HELP_VALUE),
1746 		    opt_value, arg_value },
1747 
1748 		/* dyn:delete */
1749 		{ cmd_delete, cpl_eltarg, name_delete,
1750 		    /* MSG_INTL(MSG_DESC_DELETE) */
1751 		    ELFEDIT_I18NHDL(MSG_DESC_DELETE),
1752 		    /* MSG_INTL(MSG_HELP_DELETE) */
1753 		    ELFEDIT_I18NHDL(MSG_HELP_DELETE),
1754 		    opt_minus_dynndx, arg_delete },
1755 
1756 		/* dyn:move */
1757 		{ cmd_move, cpl_eltarg, name_move,
1758 		    /* MSG_INTL(MSG_DESC_MOVE) */
1759 		    ELFEDIT_I18NHDL(MSG_DESC_MOVE),
1760 		    /* MSG_INTL(MSG_HELP_MOVE) */
1761 		    ELFEDIT_I18NHDL(MSG_HELP_MOVE),
1762 		    opt_minus_dynndx, arg_move },
1763 
1764 		/* dyn:runpath */
1765 		{ cmd_runpath, NULL, name_runpath,
1766 		    /* MSG_INTL(MSG_DESC_RUNPATH) */
1767 		    ELFEDIT_I18NHDL(MSG_DESC_RUNPATH),
1768 		    /* MSG_INTL(MSG_HELP_RUNPATH) */
1769 		    ELFEDIT_I18NHDL(MSG_HELP_RUNPATH),
1770 		    opt_ostyle, arg_runpath },
1771 
1772 		/* dyn:posflag1 */
1773 		{ cmd_posflag1, cpl_posflag1, name_posflag1,
1774 		    /* MSG_INTL(MSG_DESC_POSFLAG1) */
1775 		    ELFEDIT_I18NHDL(MSG_DESC_POSFLAG1),
1776 		    /* MSG_INTL(MSG_HELP_POSFLAG1) */
1777 		    ELFEDIT_I18NHDL(MSG_HELP_POSFLAG1),
1778 		    opt_ostyle_bitop, arg_posflag1 },
1779 
1780 		/* dyn:flags */
1781 		{ cmd_flags, cpl_flags, name_flags,
1782 		    /* MSG_INTL(MSG_DESC_FLAGS) */
1783 		    ELFEDIT_I18NHDL(MSG_DESC_FLAGS),
1784 		    /* MSG_INTL(MSG_HELP_FLAGS) */
1785 		    ELFEDIT_I18NHDL(MSG_HELP_FLAGS),
1786 		    opt_ostyle_bitop, arg_flags },
1787 
1788 		/* dyn:flags1 */
1789 		{ cmd_flags1, cpl_flags1, name_flags1,
1790 		    /* MSG_INTL(MSG_DESC_FLAGS1) */
1791 		    ELFEDIT_I18NHDL(MSG_DESC_FLAGS1),
1792 		    /* MSG_INTL(MSG_HELP_FLAGS1) */
1793 		    ELFEDIT_I18NHDL(MSG_HELP_FLAGS1),
1794 		    opt_ostyle_bitop, arg_flags1 },
1795 
1796 		/* dyn:feature1 */
1797 		{ cmd_feature1, cpl_feature1, name_feature1,
1798 		    /* MSG_INTL(MSG_DESC_FEATURE1) */
1799 		    ELFEDIT_I18NHDL(MSG_DESC_FEATURE1),
1800 		    /* MSG_INTL(MSG_HELP_FEATURE1) */
1801 		    ELFEDIT_I18NHDL(MSG_HELP_FEATURE1),
1802 		    opt_ostyle_bitop, arg_feature1 },
1803 
1804 		/* dyn:checksum */
1805 		{ cmd_checksum, NULL, name_checksum,
1806 		    /* MSG_INTL(MSG_DESC_CHECKSUM) */
1807 		    ELFEDIT_I18NHDL(MSG_DESC_CHECKSUM),
1808 		    /* MSG_INTL(MSG_HELP_CHECKSUM) */
1809 		    ELFEDIT_I18NHDL(MSG_HELP_CHECKSUM),
1810 		    NULL, NULL },
1811 
1812 		{ NULL }
1813 	};
1814 
1815 	static elfedit_module_t module = {
1816 	    ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_NAME),
1817 	    /* MSG_INTL(MSG_MOD_DESC) */
1818 	    ELFEDIT_I18NHDL(MSG_MOD_DESC), cmds, mod_i18nhdl_to_str };
1819 
1820 	return (&module);
1821 }
1822