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