xref: /titanic_52/usr/src/cmd/sgs/elfedit/modules/common/syminfo.c (revision 8e4728571e959f3afb368407b22125278412c9d4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include	<stdio.h>
29 #include	<unistd.h>
30 #include	<machdep.h>
31 #include	<elfedit.h>
32 #include	<strings.h>
33 #include	<debug.h>
34 #include	<conv.h>
35 #include	<syminfo_msg.h>
36 
37 
38 
39 /*
40  * This module uses shared code for several of the commands.
41  * It is sometimes necessary to know which specific command
42  * is active.
43  */
44 typedef enum {
45 	SYMINFO_CMD_T_DUMP =		0,	/* syminfo:dump */
46 
47 	SYMINFO_CMD_T_SI_BOUNDTO =	1,	/* syminfo:si_boundto */
48 	SYMINFO_CMD_T_SI_FLAGS =	2	/* syminfo:si_boundto */
49 } SYMINFO_CMD_T;
50 
51 
52 
53 #ifndef _ELF64
54 /*
55  * We supply this function for the msg module. Only one copy is needed.
56  */
57 const char *
58 _syminfo_msg(Msg mid)
59 {
60 	return (gettext(MSG_ORIG(mid)));
61 }
62 
63 #endif
64 
65 
66 
67 /*
68  * This function is supplied to elfedit through our elfedit_module_t
69  * definition. It translates the opaque elfedit_i18nhdl_t handles
70  * in our module interface into the actual strings for elfedit to
71  * use.
72  *
73  * note:
74  *	This module uses Msg codes for its i18n handle type.
75  *	So the translation is simply to use MSG_INTL() to turn
76  *	it into a string and return it.
77  */
78 static const char *
79 mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
80 {
81 	Msg msg = (Msg)hdl;
82 
83 	return (MSG_INTL(msg));
84 }
85 
86 
87 
88 /*
89  * The sym_opt_t enum specifies a bit value for every optional
90  * argument allowed by a command in this module.
91  */
92 typedef enum {
93 	SYMINFO_OPT_F_AND =	1,	/* -and: AND (&) values to dest */
94 	SYMINFO_OPT_F_CMP =	2,	/* -cmp: Complement (~) values */
95 	SYMINFO_OPT_F_NEEDED =	4,	/* -needed: arg is name of object to */
96 					/*	be referenced via DT_NEEDED */
97 					/*	dynamic entry */
98 	SYMINFO_OPT_F_OR =	8,	/* -or: OR (|) values to dest */
99 	SYMINFO_OPT_F_SYMNDX =	16	/* -symndx: Sym specified by index */
100 } syminfo_opt_t;
101 
102 
103 /*
104  * A variable of type ARGSTATE is used by each command to maintain
105  * information about the syminfo section being used, as and for any
106  * auxiliary sections that are related to it. This helps us to ensure
107  * that we only fetch each section a single time:
108  *	- More efficient
109  *	- Prevents multiple ELFEDIT_MSG_DEBUG messages from
110  *	  being produced for a given section.
111  */
112 typedef struct {
113 	elfedit_obj_state_t	*obj_state;
114 	syminfo_opt_t		optmask;   	/* Mask of options used */
115 	int			argc;		/* # of plain arguments */
116 	const char		**argv;		/* Plain arguments */
117 	struct {				/* Syminfo */
118 		elfedit_section_t	*sec;
119 		Syminfo			*data;
120 		Word			n;
121 	} syminfo;
122 	struct {				/* Symbol table */
123 		elfedit_section_t	*sec;
124 		Sym			*data;
125 		Word			n;
126 	} sym;
127 	struct {				/* String table */
128 		elfedit_section_t	*sec;
129 	} str;
130 	struct {				/* Dynamic section */
131 		elfedit_section_t	*sec;
132 		Dyn			*data;
133 		Word			n;
134 	} dynamic;
135 } ARGSTATE;
136 
137 
138 
139 /*
140  * Standard argument processing for syminfo module
141  *
142  * entry
143  *	obj_state, argc, argv - Standard command arguments
144  *	optmask - Mask of allowed optional arguments.
145  *	argstate - Address of ARGSTATE block to be initialized
146  *
147  * exit:
148  *	On success, *argstate is initialized. On error,
149  *	an error is issued and this routine does not return.
150  *
151  * note:
152  *	Only the syminfo section is initially referenced by
153  *	argstate. Use the argstate_add_XXX() routines below to
154  *	access any other sections needed.
155  */
156 static void
157 process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[],
158     SYMINFO_CMD_T cmd, ARGSTATE *argstate)
159 {
160 	elfedit_getopt_state_t	getopt_state;
161 	elfedit_getopt_ret_t	*getopt_ret;
162 
163 	bzero(argstate, sizeof (*argstate));
164 	argstate->obj_state = obj_state;
165 
166 	elfedit_getopt_init(&getopt_state, &argc, &argv);
167 
168 	/* Add each new option to the options mask */
169 	while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL)
170 		argstate->optmask |= getopt_ret->gor_idmask;
171 
172 	/*
173 	 * Usage error if there are too many plain arguments.
174 	 *	- syminfo:dump accepts a single argument
175 	 *	- syminfo:si_boundto accepts 2 arguments
176 	 *	- syminfo:si_flags accepts an unbounded number
177 	 */
178 	if (((cmd == SYMINFO_CMD_T_DUMP) && (argc > 1)) ||
179 	    ((cmd == SYMINFO_CMD_T_SI_BOUNDTO) && (argc > 2)))
180 		elfedit_command_usage();
181 
182 	/* If there may be an arbitrary amount of output, use a pager */
183 	if (argc == 0)
184 		elfedit_pager_init();
185 
186 	/* Return the updated values of argc/argv */
187 	argstate->argc = argc;
188 	argstate->argv = argv;
189 
190 	/* Locate the syminfo section */
191 	argstate->syminfo.sec = elfedit_sec_getsyminfo(obj_state,
192 	    &argstate->syminfo.data, &argstate->syminfo.n);
193 }
194 
195 
196 
197 /*
198  * We maintain the state of the current syminfo table in a ARGSTATE
199  * structure. A syminfo is related to the dynamic symbol table, and
200  * can reference the dynamic section of the object. We don't look those
201  * things up unless we actually need them, both to be efficient, and
202  * to prevent duplicate ELFEDIT_MSG_DEBUG messages from being issued
203  * as they are located. Hence, process_args() is used to initialze the
204  * state block with just the syminfo section, and then one of the
205  * argstate_add_XXX() functions is used as needed to fetch the
206  * additional sections.
207  *
208  * entry:
209  *	argstate - State block for current symbol table.
210  *
211  * exit:
212  *	If the needed auxiliary section is not found, an error is
213  *	issued and the argstate_add_XXX() routine does not return.
214  *	Otherwise, the fields in argstate have been filled in, ready
215  *	for use.
216  *
217  */
218 static void
219 argstate_add_sym(ARGSTATE *argstate)
220 {
221 	if (argstate->sym.sec != NULL)
222 		return;
223 
224 	argstate->sym.sec = elfedit_sec_getsymtab(argstate->obj_state,
225 	    1, argstate->syminfo.sec->sec_shdr->sh_link, NULL,
226 	    &argstate->sym.data, &argstate->sym.n, NULL);
227 }
228 static void
229 argstate_add_str(ARGSTATE *argstate)
230 {
231 	if (argstate->str.sec != NULL)
232 		return;
233 
234 	argstate_add_sym(argstate);
235 	argstate->str.sec = elfedit_sec_getstr(argstate->obj_state,
236 	    argstate->sym.sec->sec_shdr->sh_link);
237 }
238 static void
239 argstate_add_dynamic(ARGSTATE *argstate)
240 {
241 	if (argstate->dynamic.sec != NULL)
242 		return;
243 
244 	argstate->dynamic.sec = elfedit_sec_getdyn(argstate->obj_state,
245 	    &argstate->dynamic.data, &argstate->dynamic.n);
246 }
247 
248 
249 
250 /*
251  * Display syminfo section entries in the style used by elfdump.
252  *
253  * entry:
254  *	argstate - State block for current symbol table.
255  *	ndx - Index of first symbol to display
256  *	cnt - Number of symbols to display
257  */
258 static void
259 dump_syminfo(ARGSTATE *argstate, Word ndx, Word cnt)
260 {
261 	Syminfo			*syminfo;
262 	Sym			*sym;
263 	Dyn			*dyn;
264 
265 	syminfo = argstate->syminfo.data + ndx;
266 
267 	argstate_add_sym(argstate);
268 	sym = argstate->sym.data + ndx;
269 
270 	argstate_add_str(argstate);
271 
272 	argstate_add_dynamic(argstate);
273 	dyn = argstate->dynamic.data;
274 
275 	/*
276 	 * Loop through the syminfo entries.
277 	 */
278 	Elf_syminfo_title(0);
279 
280 	for (; cnt-- > 0; ndx++, syminfo++, sym++) {
281 		const char	*needed = NULL, *name;
282 
283 		name = elfedit_offset_to_str(argstate->str.sec,
284 		    sym->st_name, ELFEDIT_MSG_ERR, 0);
285 
286 		if ((syminfo->si_boundto < SYMINFO_BT_LOWRESERVE) &&
287 		    (syminfo->si_boundto < argstate->dynamic.n) &&
288 		    ((dyn[syminfo->si_boundto].d_tag == DT_NEEDED) ||
289 		    (dyn[syminfo->si_boundto].d_tag == DT_USED)))
290 			needed = elfedit_offset_to_str(argstate->str.sec,
291 			    dyn[syminfo->si_boundto].d_un.d_val,
292 			    ELFEDIT_MSG_ERR, 0);
293 		else
294 			needed = MSG_ORIG(MSG_STR_EMPTY);
295 
296 		Elf_syminfo_entry(0, ndx, syminfo, name, needed);
297 	}
298 }
299 
300 
301 
302 /*
303  * Print syminfo values, taking the calling command, and output style
304  * into account.
305  *
306  * entry:
307  *	cmd - SYMINFO_CMD_T_* value giving identify of caller
308  *	autoprint - If True, output is only produced if the elfedit
309  *		autoprint flag is set. If False, output is always produced.
310  *	argstate - State block for current symbol table.
311  *	ndx - Index of first symbol to display
312  *	cnt - Number of symbols to display
313  */
314 static void
315 print_syminfo(SYMINFO_CMD_T cmd, int autoprint, ARGSTATE *argstate,
316     Word ndx, Word cnt)
317 {
318 	elfedit_outstyle_t	outstyle;
319 	Syminfo			*syminfo;
320 
321 	if ((autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0)) ||
322 	    (cnt == 0))
323 		return;
324 
325 	/*
326 	 * Pick an output style. syminfo:dump is required to use the default
327 	 * style. The other commands use the current output style.
328 	 */
329 	outstyle = (cmd == SYMINFO_CMD_T_DUMP) ?
330 	    ELFEDIT_OUTSTYLE_DEFAULT : elfedit_outstyle();
331 
332 	/*
333 	 * If doing default output, use elfdump style where we
334 	 * show all symbol attributes. In this case, the command
335 	 * that called us doesn't matter
336 	 */
337 	if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
338 		dump_syminfo(argstate, ndx, cnt);
339 		return;
340 	}
341 
342 	syminfo = argstate->syminfo.data;
343 
344 	switch (cmd) {
345 	case SYMINFO_CMD_T_SI_BOUNDTO:
346 		if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
347 			/* Find the dynamic section and string table */
348 			argstate_add_dynamic(argstate);
349 			argstate_add_str(argstate);
350 		}
351 
352 		for (syminfo += ndx; cnt--; syminfo++) {
353 			Half bndto = syminfo->si_boundto;
354 
355 			if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
356 				const char	*str = NULL;
357 
358 				switch (bndto) {
359 				case SYMINFO_BT_SELF:
360 					str = elfedit_atoconst_value_to_str(
361 					    ELFEDIT_CONST_SYMINFO_BT,
362 					    SYMINFO_BT_SELF, 1);
363 					break;
364 				case SYMINFO_BT_PARENT:
365 					str = elfedit_atoconst_value_to_str(
366 					    ELFEDIT_CONST_SYMINFO_BT,
367 					    SYMINFO_BT_PARENT, 1);
368 					break;
369 				case SYMINFO_BT_NONE:
370 					str = elfedit_atoconst_value_to_str(
371 					    ELFEDIT_CONST_SYMINFO_BT,
372 					    SYMINFO_BT_NONE, 1);
373 					break;
374 				}
375 				if ((str == NULL) &&
376 				    (bndto < SYMINFO_BT_LOWRESERVE) &&
377 				    (argstate->dynamic.sec != NULL) &&
378 				    (bndto < argstate->dynamic.n) &&
379 				    (argstate->dynamic.data[bndto].d_tag ==
380 				    DT_NEEDED))
381 					str = elfedit_offset_to_str(
382 					    argstate->str.sec,
383 					    argstate->dynamic.data[bndto].
384 					    d_un.d_val, ELFEDIT_MSG_ERR, 0);
385 
386 				if (str != NULL) {
387 					elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
388 					    str);
389 					continue;
390 				}
391 			}
392 
393 			/*
394 			 * If we reach this point, we are either in numeric
395 			 * mode, or we were unable to find a string above.
396 			 * In either case, output as integer.
397 			 */
398 			elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL),
399 			    EC_WORD(bndto));
400 		}
401 		break;
402 
403 	case SYMINFO_CMD_T_SI_FLAGS:
404 		for (syminfo += ndx; cnt--; syminfo++) {
405 			if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
406 				Conv_syminfo_flags_buf_t buf;
407 
408 				elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
409 				    conv_syminfo_flags(syminfo->si_flags,
410 				    CONV_FMT_NOBKT, &buf));
411 			} else {
412 				elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL),
413 				    EC_WORD(syminfo->si_flags));
414 			}
415 		}
416 		break;
417 	}
418 }
419 
420 
421 /*
422  * Convert the given argument string into a symbol table index.
423  *
424  * entry:
425  *	argstate - State block for current symbol table.
426  *	arg - String containing symbol index argument.
427  *
428  * exit:
429  *	On success, returns the symbol index. On failure, an error
430  *	is issued and this routine does not return.
431  */
432 static Word
433 arg_to_symndx(ARGSTATE *argstate, const char *arg)
434 {
435 	Word symndx;
436 
437 	/*
438 	 * If the -symndx option was specified, arg is an index
439 	 * into the symbol table.
440 	 */
441 	if (argstate->optmask & SYMINFO_OPT_F_SYMNDX)
442 		return (elfedit_atoui_range(arg, MSG_ORIG(MSG_STR_SYM),
443 		    0, argstate->syminfo.n - 1, NULL));
444 
445 	/*
446 	 * arg is a symbol name. Return the index of the first symbol
447 	 * that matches
448 	 */
449 	argstate_add_sym(argstate);
450 	argstate_add_str(argstate);
451 
452 	(void) elfedit_name_to_symndx(argstate->sym.sec,
453 	    argstate->str.sec, arg, ELFEDIT_MSG_ERR, &symndx);
454 
455 	return (symndx);
456 }
457 
458 
459 /*
460  * Given a string argument representing an object, return the index of
461  * the dynamic section that should be used for the si_boundto value.
462  */
463 static Half
464 needed_to_boundto(ARGSTATE *argstate, const char *arg)
465 {
466 	Conv_inv_buf_t		inv_buf;
467 	elfedit_dyn_elt_t	strpad_elt;
468 	elfedit_dyn_elt_t	null_elt;
469 	elfedit_section_t	*dynsec;
470 	Word			null_cnt;
471 	Dyn			*dyn;
472 	Word			str_offset, ndx, numdyn;
473 	int			have_string;
474 
475 	argstate_add_str(argstate);
476 	argstate_add_dynamic(argstate);
477 	dynsec = argstate->dynamic.sec;
478 	numdyn = argstate->dynamic.n;
479 
480 	/* Locate DT_SUNW_STRPAD element if present and locate the DT_NULLs */
481 	elfedit_dyn_elt_init(&strpad_elt);
482 	elfedit_dyn_elt_init(&null_elt);
483 	null_cnt = 0;
484 	strpad_elt.dn_dyn.d_un.d_val = 0;
485 	dyn = argstate->dynamic.data;
486 	for (ndx = 0; ndx < numdyn; dyn++, ndx++) {
487 		switch (dyn->d_tag) {
488 		case DT_NULL:
489 			/* Count all the nulls, remember the first one */
490 			null_cnt++;
491 			if (!null_elt.dn_seen)
492 				elfedit_dyn_elt_save(&null_elt, ndx, dyn);
493 			break;
494 
495 		case DT_SUNW_STRPAD:
496 			elfedit_dyn_elt_save(&strpad_elt, ndx, dyn);
497 			break;
498 		}
499 	}
500 
501 	/*
502 	 * Look up the string in the string table and get its offset. If
503 	 * this succeeds, then it is possible that there is a DT_NEEDED
504 	 * dynamic entry that references it.
505 	 */
506 	have_string = elfedit_sec_findstr(argstate->str.sec,
507 	    strpad_elt.dn_dyn.d_un.d_val, arg, &str_offset) != 0;
508 	if (have_string) {
509 		dyn = argstate->dynamic.data;
510 		for (ndx = 0; ndx < numdyn; dyn++, ndx++) {
511 			if (((dyn->d_tag == DT_NEEDED) ||
512 			    (dyn->d_tag == DT_USED)) &&
513 			    (dyn->d_un.d_val == str_offset))
514 				goto done;
515 		}
516 	}
517 
518 	/*
519 	 * It doesn't already exist. We might be able to add a DT_NEEDED
520 	 * to the dynamic section if an extra DT_NULL is available.
521 	 * Otherwise, we have to fail here.
522 	 */
523 	if (null_cnt < 2)
524 		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOEXTRANULL),
525 		    EC_WORD(dynsec->sec_shndx), dynsec->sec_name);
526 
527 	/*
528 	 * If the string is not already in the string table, try to
529 	 * insert it. If it succeeds, we will convert the DT_NULL.
530 	 * Otherwise, an error will be issued and control will not
531 	 * return here.
532 	 */
533 	if (!have_string)
534 		str_offset = elfedit_dynstr_insert(dynsec,
535 		    argstate->str.sec, &strpad_elt, arg);
536 
537 	/* Convert the extra DT_NULL */
538 	ndx = null_elt.dn_ndx;
539 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CONVNULL),
540 	    EC_WORD(dynsec->sec_shndx), dynsec->sec_name, EC_WORD(ndx),
541 	    conv_dyn_tag(DT_NEEDED, argstate->obj_state->os_ehdr->e_machine,
542 	    0, &inv_buf));
543 	dyn = argstate->dynamic.data + ndx;
544 	dyn->d_tag = DT_NEEDED;
545 	dyn->d_un.d_val = str_offset;
546 	elfedit_modified_data(dynsec);
547 
548 done:
549 	elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDNEEDED),
550 	    dynsec->sec_shndx, dynsec->sec_name, ndx, arg);
551 	return (ndx);
552 }
553 
554 /*
555  * Common body for the syminfo: module commands. These commands
556  * share a large amount of common behavior, so it is convenient
557  * to centralize things and use the cmd argument to handle the
558  * small differences.
559  *
560  * entry:
561  *	cmd - One of the SYMINFO_CMD_T_* constants listed above, specifying
562  *		which command to implement.
563  *	obj_state, argc, argv - Standard command arguments
564  */
565 static elfedit_cmdret_t
566 cmd_body(SYMINFO_CMD_T cmd, elfedit_obj_state_t *obj_state,
567     int argc, const char *argv[])
568 {
569 	ARGSTATE		argstate;
570 	Word			ndx;
571 	Syminfo			*syminfo;
572 	elfedit_cmdret_t	ret = ELFEDIT_CMDRET_NONE;
573 
574 	process_args(obj_state, argc, argv, cmd, &argstate);
575 
576 	/* If there are no arguments, dump the whole table and return */
577 	if (argstate.argc == 0) {
578 		print_syminfo(cmd, 0, &argstate, 0, argstate.syminfo.n);
579 		return (ELFEDIT_CMDRET_NONE);
580 	}
581 
582 	/* The first argument is the symbol name/index */
583 	ndx = arg_to_symndx(&argstate, argstate.argv[0]);
584 
585 	/* If there is a single argument, display that item and return */
586 	if (argstate.argc == 1) {
587 		print_syminfo(cmd, 0, &argstate, ndx, 1);
588 		return (ELFEDIT_CMDRET_NONE);
589 	}
590 
591 	syminfo = &argstate.syminfo.data[ndx];
592 
593 	/*
594 	 * Syminfo [0] holds the value SYMINFO_CURRENT, as a versioning
595 	 * technique. You're not supposed to mess with it.
596 	 */
597 	if (ndx == 0)
598 		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CHGSYMINFO0),
599 		    EC_WORD(argstate.syminfo.sec->sec_shndx),
600 		    argstate.syminfo.sec->sec_name, EC_WORD(ndx));
601 
602 	/* The second value supplies a new value for the item */
603 	switch (cmd) {
604 		/*
605 		 * SYMINFO_CMD_T_DUMP can't get here: It never has more than
606 		 * one argument, and is handled above.
607 		 */
608 
609 	case SYMINFO_CMD_T_SI_BOUNDTO:
610 		{
611 			const char *name = MSG_ORIG(MSG_CMD_SI_BOUNDTO);
612 			Half boundto;
613 
614 			if (argstate.optmask & SYMINFO_OPT_F_NEEDED)
615 				boundto = needed_to_boundto(&argstate,
616 				    argstate.argv[1]);
617 			else
618 				boundto = elfedit_atoconst_range(
619 				    argstate.argv[1], MSG_ORIG(MSG_STR_VALUE),
620 				    0, 0xffff, ELFEDIT_CONST_SYMINFO_BT);
621 
622 			if (syminfo->si_boundto == boundto) {
623 				elfedit_msg(ELFEDIT_MSG_DEBUG,
624 				    MSG_INTL(MSG_DEBUG_X_OK),
625 				    argstate.syminfo.sec->sec_shndx,
626 				    argstate.syminfo.sec->sec_name, ndx, name,
627 				    syminfo->si_boundto);
628 			} else {
629 				elfedit_msg(ELFEDIT_MSG_DEBUG,
630 				    MSG_INTL(MSG_DEBUG_X_CHG),
631 				    argstate.syminfo.sec->sec_shndx,
632 				    argstate.syminfo.sec->sec_name, ndx, name,
633 				    syminfo->si_boundto, boundto);
634 				ret = ELFEDIT_CMDRET_MOD;
635 				syminfo->si_boundto = boundto;
636 			}
637 		}
638 		break;
639 
640 	case SYMINFO_CMD_T_SI_FLAGS:
641 		{
642 			Conv_syminfo_flags_buf_t flags_buf1, flags_buf2;
643 			const char *name = MSG_ORIG(MSG_CMD_SI_FLAGS);
644 			Half flags = 0;
645 			int i;
646 
647 			/* Collect the arguments */
648 			for (i = 1; i < argstate.argc; i++)
649 				flags |= (Word)
650 				    elfedit_atoconst(argstate.argv[i],
651 				    ELFEDIT_CONST_SYMINFO_FLG);
652 
653 			/* Complement the value? */
654 			if (argstate.optmask & SYMINFO_OPT_F_CMP)
655 				flags = ~flags;
656 
657 			/* Perform any requested bit operations */
658 			if (argstate.optmask & SYMINFO_OPT_F_AND)
659 				flags &= syminfo->si_flags;
660 			else if (argstate.optmask & SYMINFO_OPT_F_OR)
661 				flags |= syminfo->si_flags;
662 
663 			/* Set the value */
664 			if (syminfo->si_flags == flags) {
665 				elfedit_msg(ELFEDIT_MSG_DEBUG,
666 				    MSG_INTL(MSG_DEBUG_S_OK),
667 				    argstate.syminfo.sec->sec_shndx,
668 				    argstate.syminfo.sec->sec_name, ndx, name,
669 				    conv_syminfo_flags(syminfo->si_flags,
670 				    0, &flags_buf1));
671 			} else {
672 				elfedit_msg(ELFEDIT_MSG_DEBUG,
673 				    MSG_INTL(MSG_DEBUG_S_CHG),
674 				    argstate.syminfo.sec->sec_shndx,
675 				    argstate.syminfo.sec->sec_name, ndx, name,
676 				    conv_syminfo_flags(syminfo->si_flags,
677 				    0, &flags_buf1),
678 				    conv_syminfo_flags(flags, 0, &flags_buf2));
679 				ret = ELFEDIT_CMDRET_MOD;
680 				syminfo->si_flags = flags;
681 			}
682 		}
683 		break;
684 	}
685 
686 	/*
687 	 * If we modified the syminfo section, tell libelf.
688 	 */
689 	if (ret == ELFEDIT_CMDRET_MOD)
690 		elfedit_modified_data(argstate.syminfo.sec);
691 
692 	/* Do autoprint */
693 	print_syminfo(cmd, 1, &argstate, ndx, 1);
694 
695 	return (ret);
696 }
697 
698 
699 
700 
701 /*
702  * Command completion functions for the various commands
703  */
704 /*ARGSUSED*/
705 static void
706 cpl_si_boundto(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
707     const char *argv[], int num_opt)
708 {
709 	int i;
710 
711 	/*
712 	 * If -needed option is not present, the second argument can be
713 	 * an SYMINFO_BT_ value.
714 	 */
715 	if (argc != (num_opt + 2))
716 		return;
717 
718 	/* Is -needed there? If so, no completion is possible so return */
719 	for (i = 0; i < num_opt; i++)
720 		if (strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_NEEDED)) == 0)
721 			return;
722 
723 	elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SYMINFO_BT);
724 }
725 
726 /*ARGSUSED*/
727 static void
728 cpl_si_flags(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
729     const char *argv[], int num_opt)
730 {
731 	/* The second argument can be an SYMINFO_FLG_ value */
732 	if (argc == (num_opt + 2))
733 		elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SYMINFO_FLG);
734 }
735 
736 
737 
738 /*
739  * Implementation functions for the commands
740  */
741 static elfedit_cmdret_t
742 cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
743 {
744 	return (cmd_body(SYMINFO_CMD_T_DUMP, obj_state, argc, argv));
745 }
746 
747 
748 static elfedit_cmdret_t
749 cmd_si_boundto(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
750 {
751 	return (cmd_body(SYMINFO_CMD_T_SI_BOUNDTO, obj_state, argc, argv));
752 }
753 
754 
755 static elfedit_cmdret_t
756 cmd_si_flags(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
757 {
758 	return (cmd_body(SYMINFO_CMD_T_SI_FLAGS, obj_state, argc, argv));
759 }
760 
761 
762 
763 
764 /*ARGSUSED*/
765 elfedit_module_t *
766 elfedit_init(elfedit_module_version_t version)
767 {
768 	/* sym:dump */
769 	static const char *name_dump[] = {
770 	    MSG_ORIG(MSG_CMD_DUMP),
771 	    MSG_ORIG(MSG_STR_EMPTY),	/* "" makes this the default command */
772 	    NULL
773 	};
774 	static elfedit_cmd_optarg_t opt_dump[] = {
775 		{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
776 		    /* MSG_INTL(MSG_OPTDESC_SYMNDX) */
777 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0,
778 		    SYMINFO_OPT_F_SYMNDX, 0 },
779 		{ NULL }
780 	};
781 	static elfedit_cmd_optarg_t arg_dump[] = {
782 		{ MSG_ORIG(MSG_STR_SYM),
783 		    /* MSG_INTL(MSG_A1_SYM) */
784 		    ELFEDIT_I18NHDL(MSG_A1_SYM),
785 		    ELFEDIT_CMDOA_F_OPT },
786 		{ NULL }
787 	};
788 
789 	/* sym:si_boundto */
790 	static const char *name_si_boundto[] = {
791 	    MSG_ORIG(MSG_CMD_SI_BOUNDTO), NULL };
792 	static elfedit_cmd_optarg_t opt_si_boundto[] = {
793 		{ MSG_ORIG(MSG_STR_MINUS_NEEDED),
794 		    /* MSG_INTL(MSG_OPTDESC_NEEDED) */
795 		    ELFEDIT_I18NHDL(MSG_OPTDESC_NEEDED), 0,
796 		    SYMINFO_OPT_F_NEEDED, 0 },
797 		{ ELFEDIT_STDOA_OPT_O, NULL,
798 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
799 		{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
800 		    /* MSG_INTL(MSG_OPTDESC_SYMNDX) */
801 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0,
802 		    SYMINFO_OPT_F_SYMNDX, 0 },
803 		{ NULL }
804 	};
805 	static elfedit_cmd_optarg_t arg_si_boundto[] = {
806 		{ MSG_ORIG(MSG_STR_SYM),
807 		    /* MSG_INTL(MSG_A1_SYM) */
808 		    ELFEDIT_I18NHDL(MSG_A1_SYM),
809 		    ELFEDIT_CMDOA_F_OPT },
810 		{ MSG_ORIG(MSG_STR_VALUE),
811 		    /* MSG_INTL(MSG_A2_DESC_SI_BOUNDTO) */
812 		    ELFEDIT_I18NHDL(MSG_A2_DESC_SI_BOUNDTO),
813 		    ELFEDIT_CMDOA_F_OPT },
814 		{ NULL }
815 	};
816 
817 	/* sym:si_flags */
818 	static const char *name_si_flags[] = {
819 	    MSG_ORIG(MSG_CMD_SI_FLAGS), NULL };
820 	static elfedit_cmd_optarg_t opt_si_flags[] = {
821 		{ ELFEDIT_STDOA_OPT_AND, NULL, ELFEDIT_CMDOA_F_INHERIT,
822 		    SYMINFO_OPT_F_AND, SYMINFO_OPT_F_OR },
823 		{ ELFEDIT_STDOA_OPT_CMP, NULL,
824 		    ELFEDIT_CMDOA_F_INHERIT, SYMINFO_OPT_F_CMP, 0 },
825 		{ ELFEDIT_STDOA_OPT_O, NULL,
826 		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
827 		{ ELFEDIT_STDOA_OPT_OR, NULL, ELFEDIT_CMDOA_F_INHERIT,
828 		    SYMINFO_OPT_F_OR, SYMINFO_OPT_F_AND },
829 		{ MSG_ORIG(MSG_STR_MINUS_SYMNDX),
830 		    /* MSG_INTL(MSG_OPTDESC_SYMNDX) */
831 		    ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0,
832 		    SYMINFO_OPT_F_SYMNDX, 0 },
833 		{ NULL }
834 	};
835 	static elfedit_cmd_optarg_t arg_si_flags[] = {
836 		{ MSG_ORIG(MSG_STR_SYM),
837 		    /* MSG_INTL(MSG_A1_SYM) */
838 		    ELFEDIT_I18NHDL(MSG_A1_SYM),
839 		    ELFEDIT_CMDOA_F_OPT },
840 		{ MSG_ORIG(MSG_STR_VALUE),
841 		    /* MSG_INTL(MSG_A2_DESC_SI_FLAGS) */
842 		    ELFEDIT_I18NHDL(MSG_A2_DESC_SI_FLAGS),
843 		    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
844 		{ NULL }
845 	};
846 
847 	static elfedit_cmd_t cmds[] = {
848 		/* sym:dump */
849 		{ cmd_dump, NULL, name_dump,
850 		    /* MSG_INTL(MSG_DESC_DUMP) */
851 		    ELFEDIT_I18NHDL(MSG_DESC_DUMP),
852 		    /* MSG_INTL(MSG_HELP_DUMP) */
853 		    ELFEDIT_I18NHDL(MSG_HELP_DUMP),
854 		    opt_dump, arg_dump },
855 
856 		/* sym:si_boundto */
857 		{ cmd_si_boundto, cpl_si_boundto, name_si_boundto,
858 		    /* MSG_INTL(MSG_DESC_SI_BOUNDTO) */
859 		    ELFEDIT_I18NHDL(MSG_DESC_SI_BOUNDTO),
860 		    /* MSG_INTL(MSG_HELP_SI_BOUNDTO) */
861 		    ELFEDIT_I18NHDL(MSG_HELP_SI_BOUNDTO),
862 		    opt_si_boundto, arg_si_boundto },
863 
864 		/* sym:si_flags */
865 		{ cmd_si_flags, cpl_si_flags, name_si_flags,
866 		    /* MSG_INTL(MSG_DESC_SI_FLAGS) */
867 		    ELFEDIT_I18NHDL(MSG_DESC_SI_FLAGS),
868 		    /* MSG_INTL(MSG_HELP_SI_FLAGS) */
869 		    ELFEDIT_I18NHDL(MSG_HELP_SI_FLAGS),
870 		    opt_si_flags, arg_si_flags },
871 
872 		{ NULL }
873 	};
874 
875 	static elfedit_module_t module = {
876 	    ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_NAME),
877 	    /* MSG_INTL(MSG_MOD_DESC) */
878 	    ELFEDIT_I18NHDL(MSG_MOD_DESC),
879 	    cmds, mod_i18nhdl_to_str };
880 
881 	return (&module);
882 }
883