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 */
42typedef 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 */
55const 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 */
76static const char *
77mod_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 */
90typedef 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 */
110typedef 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 */
154static void
155process_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 */
216static void
217argstate_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}
226static void
227argstate_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}
236static void
237argstate_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 */
256static void
257dump_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 */
312static void
313print_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 */
430static Word
431arg_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 */
461static Half
462needed_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
550done:
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 */
567static elfedit_cmdret_t
568cmd_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*/
707static void
708cpl_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*/
729static void
730cpl_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 */
743static elfedit_cmdret_t
744cmd_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
750static elfedit_cmdret_t
751cmd_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
757static elfedit_cmdret_t
758cmd_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*/
767elfedit_module_t *
768elfedit_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, NULL,
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, NULL, ELFEDIT_CMDOA_F_INHERIT,
824		    SYMINFO_OPT_F_AND, SYMINFO_OPT_F_OR },
825		{ ELFEDIT_STDOA_OPT_CMP, NULL,
826		    ELFEDIT_CMDOA_F_INHERIT, SYMINFO_OPT_F_CMP, 0 },
827		{ ELFEDIT_STDOA_OPT_O, NULL,
828		    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
829		{ ELFEDIT_STDOA_OPT_OR, NULL, 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