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