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