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 <ctype.h>
29 #include <unistd.h>
30 #include <elfedit.h>
31 #include <strings.h>
32 #include <debug.h>
33 #include <conv.h>
34 #include <str_msg.h>
35
36
37
38
39 #define MAXNDXSIZE 10
40
41
42
43 /*
44 * This module uses shared code for several of the commands.
45 * It is sometimes necessary to know which specific command
46 * is active.
47 */
48 typedef enum {
49 STR_CMD_T_DUMP = 0, /* str:dump */
50 STR_CMD_T_SET = 1, /* str:set */
51 STR_CMD_T_ADD = 2, /* str:add */
52 STR_CMD_T_ZERO = 3, /* str:zero */
53 } STR_CMD_T;
54
55
56
57 #ifndef _ELF64
58 /*
59 * We supply this function for the msg module. Only one copy is needed.
60 */
61 const char *
_str_msg(Msg mid)62 _str_msg(Msg mid)
63 {
64 return (gettext(MSG_ORIG(mid)));
65 }
66
67 #endif
68
69
70
71 /*
72 * This function is supplied to elfedit through our elfedit_module_t
73 * definition. It translates the opaque elfedit_i18nhdl_t handles
74 * in our module interface into the actual strings for elfedit to
75 * use.
76 *
77 * note:
78 * This module uses Msg codes for its i18n handle type.
79 * So the translation is simply to use MSG_INTL() to turn
80 * it into a string and return it.
81 */
82 static const char *
mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)83 mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
84 {
85 Msg msg = (Msg)hdl;
86
87 return (MSG_INTL(msg));
88 }
89
90
91
92 /*
93 * The sym_opt_t enum specifies a bit value for every optional
94 * argument allowed by a command in this module.
95 */
96 typedef enum {
97 STR_OPT_F_ANY = 1, /* -any: treat any sec. as strtab */
98 STR_OPT_F_END = 2, /* -end: zero to end of strtab */
99 STR_OPT_F_NOTERM = 4, /* -noterm: str:set won't term string */
100 STR_OPT_F_SHNAME = 8, /* -shnam name: section spec. by name */
101 STR_OPT_F_SHNDX = 16, /* -shndx ndx: strtab spec. by index */
102 STR_OPT_F_SHTYP = 32, /* -shtyp type: section spec. by type */
103 STR_OPT_F_STRNDX = 64, /* -strndx: String specified by index */
104 } str_opt_t;
105
106
107 /*
108 * A variable of type ARGSTATE is used by each command to maintain
109 * information about the string table section being used, and for any
110 * auxiliary sections that are related to it.
111 */
112 typedef struct {
113 elfedit_obj_state_t *obj_state;
114 str_opt_t optmask; /* Mask of options used */
115 int argc; /* # of plain arguments */
116 const char **argv; /* Plain arguments */
117
118 struct { /* String table */
119 elfedit_section_t *sec;
120 Word ndx; /* Table offset if (argc > 0) */
121 } str;
122 struct { /* Dynamic section */
123 elfedit_section_t *sec;
124 Dyn *data;
125 Word n;
126 elfedit_dyn_elt_t strpad;
127 } dyn;
128 } ARGSTATE;
129
130
131
132 /*
133 * Given an ELF SHT_ section type constant, shndx_to_strtab() returns
134 * one of the following
135 */
136
137 typedef enum {
138 SHTOSTR_NONE = 0, /* Type can't lead to a string table */
139 SHTOSTR_STRTAB = 1, /* type is SHT_STRTAB */
140 SHTOSTR_LINK_STRTAB = 2, /* sh_link for type yields strtab */
141 SHTOSTR_LINK_SYMTAB = 3, /* sh_link for type yields symtab */
142 SHTOSTR_SHF_STRINGS = 4, /* Not strtab, but SHF_STRINGS set */
143 } SHTOSTR_T;
144
145 static SHTOSTR_T
shtype_to_strtab(Word sh_type,Word sh_flags)146 shtype_to_strtab(Word sh_type, Word sh_flags)
147 {
148 /*
149 * A string table section always leads to itself. A
150 * non-string table that has it's SHF_STRINGS section flag
151 * set trumps anything else.
152 */
153 if (sh_type == SHT_STRTAB)
154 return (SHTOSTR_STRTAB);
155 if (sh_flags & SHF_STRINGS)
156 return (SHTOSTR_SHF_STRINGS);
157
158 /*
159 * Look at non-stringtable section types that can lead to
160 * string tables via sh_link.
161 */
162 switch (sh_type) {
163 /* These sections reference a string table via sh_link */
164 case SHT_DYNAMIC:
165 case SHT_SYMTAB:
166 case SHT_DYNSYM:
167 case SHT_SUNW_LDYNSYM:
168 case SHT_SUNW_verdef:
169 case SHT_SUNW_verneed:
170 return (SHTOSTR_LINK_STRTAB);
171
172 /*
173 * These sections reference a symbol table via sh_link.
174 * Symbol tables, in turn, reference a string table
175 * via their sh_link.
176 */
177 case SHT_HASH:
178 case SHT_REL:
179 case SHT_RELA:
180 case SHT_GROUP:
181 case SHT_SYMTAB_SHNDX:
182 case SHT_SUNW_move:
183 case SHT_SUNW_syminfo:
184 case SHT_SUNW_versym:
185 case SHT_SUNW_symsort:
186 case SHT_SUNW_tlssort:
187 return (SHTOSTR_LINK_SYMTAB);
188 }
189
190 /* Types that lead to string tables were caught above */
191 return (SHTOSTR_NONE);
192 }
193
194 /*
195 * Given a section index, attempt to convert it into an index
196 * to a string table section.
197 */
198 static Word
shndx_to_strtab(elfedit_obj_state_t * obj_state,Word ndx)199 shndx_to_strtab(elfedit_obj_state_t *obj_state, Word ndx)
200 {
201 /*
202 * Locate and validate the string table. In the case where
203 * a non-string table section is given that references a string
204 * table, we will use the referenced table.
205 */
206 if (ndx < obj_state->os_shnum) {
207 Shdr *shdr = obj_state->os_secarr[ndx].sec_shdr;
208
209 switch (shtype_to_strtab(shdr->sh_type, shdr->sh_flags)) {
210
211 /* Sections that reference a string table via sh_link */
212 case SHTOSTR_LINK_STRTAB:
213 ndx = shdr->sh_link;
214 break;
215
216 /*
217 * Sections that reference a symbol tabel via sh_link,
218 * which in turn reference a string table via their sh_link.
219 */
220 case SHTOSTR_LINK_SYMTAB:
221 ndx = shdr->sh_link;
222 if (ndx < obj_state->os_shnum)
223 ndx =
224 obj_state->os_secarr[ndx].sec_shdr->sh_link;
225 break;
226 }
227 }
228
229 return (ndx);
230 }
231
232
233
234 /*
235 * Standard argument processing for string table module
236 *
237 * entry
238 * obj_state, argc, argv - Standard command arguments
239 * optmask - Mask of allowed optional arguments.
240 * argstate - Address of ARGSTATE block to be initialized
241 *
242 * exit:
243 * On success, *argstate is initialized. On error,
244 * an error is issued and this routine does not return.
245 */
246 static void
process_args(elfedit_obj_state_t * obj_state,int argc,const char * argv[],STR_CMD_T cmd,ARGSTATE * argstate,int * print_only)247 process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[],
248 STR_CMD_T cmd, ARGSTATE *argstate, int *print_only)
249 {
250 elfedit_getopt_state_t getopt_state;
251 elfedit_getopt_ret_t *getopt_ret;
252 Word ndx;
253 int argc_ok;
254
255 bzero(argstate, sizeof (*argstate));
256 argstate->obj_state = obj_state;
257
258 /*
259 * By default, we use the section name string table pointed at
260 * by the ELF header.
261 */
262 ndx = obj_state->os_ehdr->e_shstrndx;
263
264 elfedit_getopt_init(&getopt_state, &argc, &argv);
265
266 /* Add each new option to the options mask */
267 while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
268 argstate->optmask |= getopt_ret->gor_idmask;
269
270 switch (getopt_ret->gor_idmask) {
271 case STR_OPT_F_SHNAME: /* -shnam name */
272 ndx = elfedit_name_to_shndx(obj_state,
273 getopt_ret->gor_value);
274 break;
275
276 case STR_OPT_F_SHNDX: /* -shndx index */
277 ndx = elfedit_atoui(getopt_ret->gor_value, NULL);
278 break;
279
280 case STR_OPT_F_SHTYP: /* -shtyp type */
281 ndx = elfedit_type_to_shndx(obj_state,
282 elfedit_atoconst(getopt_ret->gor_value,
283 ELFEDIT_CONST_SHT));
284 break;
285 }
286 }
287
288 /*
289 * Usage error if there are the wrong number of plain arguments.
290 */
291 switch (cmd) {
292 case STR_CMD_T_DUMP:
293 argc_ok = (argc == 0) || (argc == 1);
294 *print_only = 1;
295 break;
296 case STR_CMD_T_SET:
297 argc_ok = (argc == 1) || (argc == 2);
298 *print_only = (argc == 1);
299 break;
300 case STR_CMD_T_ADD:
301 argc_ok = (argc == 1);
302 *print_only = 0;
303 break;
304 case STR_CMD_T_ZERO:
305 /*
306 * The second argument (count) and the -end option are
307 * mutally exclusive.
308 */
309 argc_ok = ((argc == 1) || (argc == 2)) &&
310 !((argc == 2) && (argstate->optmask & STR_OPT_F_END));
311 *print_only = 0;
312 break;
313 default:
314 argc_ok = 0; /* Unknown command? */
315 break;
316 }
317 if (!argc_ok)
318 elfedit_command_usage();
319
320 /* If there may be an arbitrary amount of output, use a pager */
321 if (argc == 0)
322 elfedit_pager_init();
323
324 /* Return the updated values of argc/argv */
325 argstate->argc = argc;
326 argstate->argv = argv;
327
328 if (argstate->optmask & STR_OPT_F_ANY) {
329 /* Take the arbitrary section */
330 argstate->str.sec = elfedit_sec_get(obj_state, ndx);
331
332 } else {
333 /*
334 * Locate and validate the string table. In the case where
335 * a non-string table section is given that references a string
336 * table, we will use the referenced table.
337 */
338 ndx = shndx_to_strtab(obj_state, ndx);
339
340 /*
341 * If ndx is a string table, the following will issue the
342 * proper debug messages. If it is out of range, or of any
343 * other type, an error is issued and it doesn't return.
344 */
345 argstate->str.sec = elfedit_sec_getstr(obj_state, ndx, 1);
346 }
347
348 /*
349 * If there is a dynamic section, check its sh_link to the
350 * string table index. If these match, then we have the
351 * dynamic string table. In that case, fetch the dynamic
352 * section and locate the DT_SUNW_STRPAD entry, causing
353 * debug messages to be issued.
354 */
355 argstate->dyn.sec = NULL;
356 elfedit_dyn_elt_init(&argstate->dyn.strpad);
357 if (obj_state->os_dynndx != SHN_UNDEF) {
358 elfedit_section_t *dynsec =
359 &obj_state->os_secarr[obj_state->os_dynndx];
360
361 if ((dynsec->sec_shdr->sh_type == SHT_DYNAMIC) &&
362 (argstate->str.sec->sec_shndx ==
363 dynsec->sec_shdr->sh_link)) {
364 argstate->dyn.sec = elfedit_sec_getdyn(obj_state,
365 &argstate->dyn.data, &argstate->dyn.n);
366 (void) elfedit_dynstr_getpad(obj_state, dynsec,
367 &argstate->dyn.strpad);
368
369 /*
370 * Does the pad value make sense?
371 * Issue debug message and ignore it if not.
372 */
373 if ((argstate->dyn.strpad.dn_seen != 0) &&
374 (argstate->dyn.strpad.dn_dyn.d_un.d_val >
375 argstate->str.sec->sec_data->d_size)) {
376 argstate->dyn.strpad.dn_seen = 0;
377 elfedit_msg(ELFEDIT_MSG_DEBUG,
378 MSG_INTL(MSG_DEBUG_BADSTRPAD),
379 EC_WORD(argstate->str.sec->sec_shndx),
380 argstate->str.sec->sec_name,
381 EC_XWORD(argstate->dyn.strpad.dn_dyn.
382 d_un.d_val),
383 EC_XWORD(argstate->str.sec->
384 sec_data->d_size));
385
386 }
387 }
388 }
389
390 /* Locate the string table offset if argument is present */
391 if ((argc > 0) && (cmd != STR_CMD_T_ADD)) {
392 /*
393 * If the -strndx option was specified, arg is an index
394 * into the string table. Otherwise it is a string
395 * to be looked up.
396 */
397 if (argstate->optmask & STR_OPT_F_STRNDX) {
398 argstate->str.ndx = (elfedit_atoui_range(argv[0],
399 MSG_ORIG(MSG_STR_STRING), 0,
400 argstate->str.sec->sec_data->d_size - 1, NULL));
401 } else {
402 if (elfedit_sec_findstr(argstate->str.sec, 0, argv[0],
403 &argstate->str.ndx) == 0)
404 elfedit_msg(ELFEDIT_MSG_ERR,
405 MSG_INTL(MSG_ERR_STRNOTFND),
406 EC_WORD(argstate->str.sec->sec_shndx),
407 argstate->str.sec->sec_name, argv[0]);
408 }
409 } else {
410 argstate->str.ndx = 0;
411 }
412 }
413
414
415
416 /*
417 * Print string table values, taking output style into account.
418 *
419 * entry:
420 * autoprint - If True, output is only produced if the elfedit
421 * autoprint flag is set. If False, output is always produced.
422 * argstate - State block for current symbol table.
423 */
424 static void
print_strtab(int autoprint,ARGSTATE * argstate)425 print_strtab(int autoprint, ARGSTATE *argstate)
426 {
427 char index[(MAXNDXSIZE * 2) + 4];
428 elfedit_outstyle_t outstyle;
429 const char *str, *limit, *tbl_limit;
430 Word ndx;
431
432
433 if (autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0))
434 return;
435
436 outstyle = elfedit_outstyle();
437 if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
438 elfedit_printf(MSG_INTL(MSG_FMT_STRTAB),
439 argstate->str.sec->sec_name);
440 if (argstate->dyn.strpad.dn_seen)
441 elfedit_printf(MSG_INTL(MSG_FMT_DYNSTRPAD),
442 EC_WORD(argstate->str.sec->sec_data->d_size -
443 argstate->dyn.strpad.dn_dyn.d_un.d_val),
444 EC_WORD(argstate->str.sec->sec_data->d_size - 1),
445 EC_WORD(argstate->dyn.strpad.dn_dyn.d_un.d_val));
446 elfedit_printf(MSG_INTL(MSG_FMT_DUMPTITLE));
447 }
448
449 str = argstate->str.sec->sec_data->d_buf;
450 tbl_limit = str + argstate->str.sec->sec_data->d_size;
451 ndx = argstate->str.ndx;
452 if (argstate->argc > 0) {
453 str += ndx;
454 /*
455 * If first byte is NULL and this is the default output style,
456 * then we want to display the range of NULL bytes, and we
457 * push limit out to the last one in the sequence. Otherwise,
458 * just display the string.
459 */
460 if ((*str == '\0') && (outstyle == ELFEDIT_OUTSTYLE_DEFAULT)) {
461 limit = str;
462 while (((limit + 1) < tbl_limit) &&
463 (*(limit + 1) == '\0'))
464 limit++;
465 } else {
466 limit = str + strlen(str) + 1;
467 }
468 } else {
469 /* Display the entire string table */
470 limit = tbl_limit;
471 }
472
473
474 while (str < limit) {
475 Word skip = strlen(str) + 1;
476 Word start_ndx;
477
478 if (outstyle != ELFEDIT_OUTSTYLE_DEFAULT) {
479 elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), str);
480 str += skip;
481 ndx += skip;
482 continue;
483 }
484
485 start_ndx = ndx;
486 if (*str == '\0')
487 while (((str + 1) < limit) && (*(str + 1) == '\0')) {
488 ndx++;
489 str++;
490 }
491
492 if (start_ndx != ndx) {
493 (void) snprintf(index, sizeof (index),
494 MSG_ORIG(MSG_FMT_INDEXRANGE),
495 EC_XWORD(start_ndx), EC_XWORD(ndx));
496 } else {
497 (void) snprintf(index, sizeof (index),
498 MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(ndx));
499 }
500 elfedit_printf(MSG_ORIG(MSG_FMT_DUMPENTRY), index);
501 elfedit_write(MSG_ORIG(MSG_STR_DQUOTE), MSG_STR_DQUOTE_SIZE);
502 if (start_ndx == ndx)
503 elfedit_str_to_c_literal(str, elfedit_write);
504 elfedit_write(MSG_ORIG(MSG_STR_DQUOTENL),
505 MSG_STR_DQUOTENL_SIZE);
506 str += skip;
507 ndx += skip;
508 }
509 }
510
511
512 /*
513 * Command body for str:set, handling the case where the 3rd
514 * argument (new-str) is present.
515 */
516 static elfedit_cmdret_t
cmd_body_set(ARGSTATE * argstate)517 cmd_body_set(ARGSTATE *argstate)
518 {
519 elfedit_section_t *strsec = argstate->str.sec;
520 const char *newstr = argstate->argv[1];
521 Word ndx = argstate->str.ndx;
522 char *oldstr;
523 int i, len, ncp;
524
525 len = strlen(newstr);
526 ncp = len;
527 if (!(argstate->optmask & STR_OPT_F_NOTERM))
528 ncp++;
529
530 /* NULL string with no termination? Nothing to do */
531 if (ncp == 0)
532 return (ELFEDIT_CMDRET_NONE);
533
534 /* Does it fit? */
535 if ((ndx + ncp) > strsec->sec_data->d_size)
536 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFIT),
537 EC_WORD(strsec->sec_shndx), strsec->sec_name,
538 EC_WORD(ndx), newstr);
539
540 /* Does it clobber the final NULL termination? */
541 if (((ndx + ncp) == strsec->sec_data->d_size) &&
542 (argstate->optmask & STR_OPT_F_NOTERM))
543 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FINALNULL),
544 EC_WORD(strsec->sec_shndx), strsec->sec_name,
545 EC_WORD(ndx), newstr);
546
547 /*
548 * strtab[0] is always supposed to contain a NULL byte. You're not
549 * supposed to mess with it. We will carry out this operation,
550 * but with a debug message indicating that it is unorthodox.
551 */
552 if ((ndx == 0) && (*newstr != '\0'))
553 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CHGSTR0),
554 EC_WORD(strsec->sec_shndx), strsec->sec_name,
555 EC_WORD(ndx), newstr);
556
557 /* Does it alter the existing value? */
558 oldstr = ndx + (char *)strsec->sec_data->d_buf;
559 for (i = 0; i < ncp; i++)
560 if (newstr[i] != oldstr[i])
561 break;
562 if (i == ncp) { /* No change */
563 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK),
564 strsec->sec_shndx, strsec->sec_name, ndx, newstr);
565 return (ELFEDIT_CMDRET_NONE);
566 }
567
568 /*
569 * If the new string is longer than the old one, then it will
570 * clobber the start of the following string. The resulting
571 * string table is perfectly legal, but issue a debug message
572 * letting the user know.
573 */
574 i = strlen(oldstr);
575 if (len > i)
576 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LONGSTR),
577 EC_WORD(strsec->sec_shndx), strsec->sec_name,
578 EC_WORD(ndx), len, i);
579
580 /*
581 * If we have strayed into the reserved part of the dynstr, then
582 * update DT_SUNW_STRPAD.
583 */
584 if (argstate->dyn.strpad.dn_seen) {
585 elfedit_dyn_elt_t *strpad = &argstate->dyn.strpad;
586 Word new_pad_ndx = ndx + len + 1;
587 Word pad_ndx = argstate->str.sec->sec_data->d_size -
588 strpad->dn_dyn.d_un.d_val;
589
590 if (new_pad_ndx > pad_ndx) {
591 elfedit_msg(ELFEDIT_MSG_DEBUG,
592 MSG_INTL(MSG_DEBUG_ADDDYNSTR),
593 EC_WORD(strsec->sec_shndx), strsec->sec_name,
594 EC_WORD(ndx), EC_WORD(new_pad_ndx - pad_ndx),
595 EC_WORD(strpad->dn_dyn.d_un.d_val),
596 newstr);
597
598 strpad->dn_dyn.d_un.d_val =
599 argstate->dyn.data[strpad->dn_ndx].d_un.d_val =
600 (argstate->str.sec->sec_data->d_size - new_pad_ndx);
601 elfedit_modified_data(argstate->dyn.sec);
602 }
603 }
604
605
606
607 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG),
608 strsec->sec_shndx, strsec->sec_name, ndx, len, oldstr, newstr);
609 bcopy(newstr, oldstr, ncp);
610
611 return (ELFEDIT_CMDRET_MOD);
612 }
613
614
615 /*
616 * Command body for str:zero
617 */
618 static elfedit_cmdret_t
cmd_body_zero(ARGSTATE * argstate)619 cmd_body_zero(ARGSTATE *argstate)
620 {
621 elfedit_section_t *strsec = argstate->str.sec;
622 Word count;
623 Word ndx = argstate->str.ndx;
624 char *oldstr = ndx + (char *)strsec->sec_data->d_buf;
625 Word i;
626
627 /* How many bytes to zero? */
628 if (argstate->optmask & STR_OPT_F_END)
629 count = strsec->sec_data->d_size - argstate->str.ndx;
630 else if (argstate->argc == 2)
631 count = elfedit_atoui_range(argstate->argv[1],
632 MSG_ORIG(MSG_STR_COUNT), 0,
633 argstate->str.sec->sec_data->d_size - argstate->str.ndx,
634 NULL);
635 else
636 count = strlen(oldstr);
637
638 /* Does it alter the existing value? */
639 for (i = 0; i < count; i++)
640 if (oldstr[i] != '\0')
641 break;
642 if (i == count) { /* No change */
643 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_Z_OK),
644 strsec->sec_shndx, strsec->sec_name, ndx);
645 return (ELFEDIT_CMDRET_NONE);
646 }
647
648 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_Z_CHG),
649 strsec->sec_shndx, strsec->sec_name, ndx, count);
650 bzero(oldstr, count);
651
652 return (ELFEDIT_CMDRET_MOD);
653 }
654
655
656 /*
657 * Common body for the str: module commands.
658 *
659 * entry:
660 * cmd - One of the STR_CMD_T_* constants listed above, specifying
661 * which command to implement.
662 * obj_state, argc, argv - Standard command arguments
663 */
664 static elfedit_cmdret_t
cmd_body(STR_CMD_T cmd,elfedit_obj_state_t * obj_state,int argc,const char * argv[])665 cmd_body(STR_CMD_T cmd, elfedit_obj_state_t *obj_state,
666 int argc, const char *argv[])
667 {
668 ARGSTATE argstate;
669 elfedit_cmdret_t ret = ELFEDIT_CMDRET_NONE;
670 int print_only;
671
672 process_args(obj_state, argc, argv, cmd, &argstate, &print_only);
673
674 /*
675 * If this call call does not change data, display the current
676 * value(s) and return.
677 */
678 if (print_only) {
679 print_strtab(0, &argstate);
680 return (ELFEDIT_CMDRET_NONE);
681 }
682
683 switch (cmd) {
684 /* NOTE: STR_CMD_T_DUMP can't get here --- it's always print_only */
685
686 case STR_CMD_T_SET:
687 ret = cmd_body_set(&argstate);
688 break;
689
690 case STR_CMD_T_ADD:
691 argstate.str.ndx = elfedit_strtab_insert(obj_state,
692 argstate.str.sec, argstate.dyn.sec, argstate.argv[0]);
693 break;
694
695 case STR_CMD_T_ZERO:
696 ret = cmd_body_zero(&argstate);
697 break;
698 }
699
700 /*
701 * If we modified the strtab section, tell libelf.
702 */
703 if (ret == ELFEDIT_CMDRET_MOD)
704 elfedit_modified_data(argstate.str.sec);
705
706 /* Do autoprint */
707 print_strtab(1, &argstate);
708
709 return (ret);
710 }
711
712
713
714
715 /*
716 * Command completion functions for the various commands
717 */
718
719 static void
add_shtyp_match(Word sh_type,void * cpldata)720 add_shtyp_match(Word sh_type, void *cpldata)
721 {
722 char buf[128];
723 const char *s;
724 char *s2;
725
726 s = elfedit_atoconst_value_to_str(ELFEDIT_CONST_SHT, sh_type, 0);
727 elfedit_cpl_match(cpldata, s, 1);
728
729 /*
730 * To get the informal tag names that are lowercase
731 * and lack the leading SHT_, we copy the string we
732 * have into a buffer and process it.
733 */
734 if (strlen(s) < 4)
735 return;
736 (void) strlcpy(buf, s + 4, sizeof (buf));
737 for (s2 = buf; *s2 != '\0'; s2++)
738 if (isupper(*s2))
739 *s2 = tolower(*s2);
740 elfedit_cpl_match(cpldata, buf, 1);
741 }
742
743 /*
744 * Handle filling in the values for -shnam, -shndx, and -shtyp options.
745 */
746 /*ARGSUSED*/
747 static void
cpl_sh_opt(elfedit_obj_state_t * obj_state,void * cpldata,int argc,const char * argv[],int num_opt)748 cpl_sh_opt(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
749 const char *argv[], int num_opt)
750 {
751 enum { NAME, INDEX, TYPE } op;
752 elfedit_section_t *sec;
753 Word ndx;
754
755 if ((argc != num_opt) || (argc < 2))
756 return;
757
758 if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNAM)) == 0) {
759 op = NAME;
760 } else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0) {
761 op = INDEX;
762
763 } else if (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 0) {
764 op = TYPE;
765
766 if (obj_state == NULL) { /* No object available */
767 elfedit_atoui_sym_t *atoui_sym;
768
769 atoui_sym = elfedit_const_to_atoui(ELFEDIT_CONST_SHT);
770 for (; atoui_sym->sym_name != NULL; atoui_sym++)
771 if (shtype_to_strtab(atoui_sym->sym_value, 0) !=
772 SHTOSTR_NONE)
773 elfedit_cpl_match(cpldata,
774 atoui_sym->sym_name, 1);
775 }
776 } else {
777 return;
778 }
779
780 if (obj_state == NULL) /* No object available */
781 return;
782
783 /*
784 * Loop over the section headers and supply command completion
785 * for the items in the file that can yield a string table.
786 */
787 sec = obj_state->os_secarr;
788 for (ndx = 0; ndx < obj_state->os_shnum; ndx++, sec++) {
789 Shdr *shdr = sec->sec_shdr;
790 SHTOSTR_T shtostr_type;
791
792 shtostr_type = shtype_to_strtab(shdr->sh_type, shdr->sh_flags);
793 if (shtostr_type == SHTOSTR_NONE)
794 continue;
795
796 switch (op) {
797 case NAME:
798 elfedit_cpl_match(cpldata, sec->sec_name, 0);
799 break;
800 case INDEX:
801 elfedit_cpl_ndx(cpldata, sec->sec_shndx);
802 break;
803 case TYPE:
804 if (shtostr_type != SHTOSTR_SHF_STRINGS)
805 add_shtyp_match(shdr->sh_type, cpldata);
806 break;
807 }
808 }
809 }
810
811
812 /*
813 * Most of the commands accept an -shXXX option for the string table
814 * and a string first argument. This routine examines which argument
815 * is being processed, and supplies completion for these items.
816 */
817 static void
cpl_sec_str(elfedit_obj_state_t * obj_state,void * cpldata,int argc,const char * argv[],int num_opt)818 cpl_sec_str(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
819 const char *argv[], int num_opt)
820 {
821 const char *str, *limit;
822 elfedit_section_t *sec;
823 Word strtab_ndx;
824 Word ndx;
825
826 /* Handle -shXXX options */
827 cpl_sh_opt(obj_state, cpldata, argc, argv, num_opt);
828
829 /* Without object state, there's no data to work from */
830 if (obj_state == NULL)
831 return;
832
833 /* If not first plain arg, return */
834 if (argc != (num_opt + 1))
835 return;
836
837 /*
838 * Look at the options, looking for two things:
839 * 1) A -shXXX option specifying a section. If so, turn that
840 * into a section index if possible.
841 * 2) Was -strndx used? If so, we are looking at an integer
842 * value and have nothing to complete.
843 */
844 strtab_ndx = obj_state->os_ehdr->e_shstrndx;
845 for (ndx = 0; ndx < num_opt; ndx++) {
846 if (strcmp(argv[ndx], MSG_ORIG(MSG_STR_MINUS_STRNDX)) == 0)
847 return;
848
849 if ((ndx+1) < num_opt) {
850 if (strcmp(argv[ndx],
851 MSG_ORIG(MSG_STR_MINUS_SHNAM)) == 0) {
852 Word i;
853
854 for (i = 1; i < obj_state->os_shnum; i++)
855 if (strcmp(obj_state->os_secarr[i].
856 sec_name, argv[ndx+1]) == 0) {
857 strtab_ndx = i;
858 break;
859 }
860 } else if (strcmp(argv[ndx],
861 MSG_ORIG(MSG_STR_MINUS_SHNDX)) == 0) {
862 elfedit_atoui_t val;
863
864 if (elfedit_atoui2(argv[ndx+1], NULL,
865 &val) != 0)
866 strtab_ndx = val;
867 } else if (strcmp(argv[ndx],
868 MSG_ORIG(MSG_STR_MINUS_SHTYP)) == 0) {
869 elfedit_atoui_t sh_type;
870 Word i;
871
872 if (elfedit_atoconst2(argv[ndx+1],
873 ELFEDIT_CONST_SHT, &sh_type) == 0)
874 continue;
875 for (i = 1; i < obj_state->os_shnum; i++)
876 if (obj_state->os_secarr[i].sec_shdr->
877 sh_type == sh_type) {
878 strtab_ndx = i;
879 break;
880 }
881 }
882 }
883 }
884
885 /*
886 * Locate and validate the string table. In the case where
887 * a non-string table section is given that references a string
888 * table, we will use the referenced table.
889 */
890 strtab_ndx = shndx_to_strtab(obj_state, strtab_ndx);
891 if ((strtab_ndx >= obj_state->os_shnum) ||
892 (obj_state->os_secarr[strtab_ndx].sec_shdr->sh_type != SHT_STRTAB))
893 return;
894 sec = &obj_state->os_secarr[strtab_ndx];
895
896 str = sec->sec_data->d_buf;
897 limit = str + sec->sec_data->d_size;
898 while (str < limit) {
899 if (*str != '\0')
900 elfedit_cpl_match(cpldata, str, 0);
901 str += strlen(str) + 1;
902 }
903 }
904
905
906
907 /*
908 * Implementation functions for the commands
909 */
910 static elfedit_cmdret_t
cmd_dump(elfedit_obj_state_t * obj_state,int argc,const char * argv[])911 cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
912 {
913 return (cmd_body(STR_CMD_T_DUMP, obj_state, argc, argv));
914 }
915
916 static elfedit_cmdret_t
cmd_set(elfedit_obj_state_t * obj_state,int argc,const char * argv[])917 cmd_set(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
918 {
919 return (cmd_body(STR_CMD_T_SET, obj_state, argc, argv));
920 }
921
922 static elfedit_cmdret_t
cmd_add(elfedit_obj_state_t * obj_state,int argc,const char * argv[])923 cmd_add(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
924 {
925 return (cmd_body(STR_CMD_T_ADD, obj_state, argc, argv));
926 }
927
928 static elfedit_cmdret_t
cmd_zero(elfedit_obj_state_t * obj_state,int argc,const char * argv[])929 cmd_zero(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
930 {
931 return (cmd_body(STR_CMD_T_ZERO, obj_state, argc, argv));
932 }
933
934
935
936 /*ARGSUSED*/
937 elfedit_module_t *
elfedit_init(elfedit_module_version_t version)938 elfedit_init(elfedit_module_version_t version)
939 {
940 /* str:dump */
941 static const char *name_dump[] = {
942 MSG_ORIG(MSG_CMD_DUMP),
943 MSG_ORIG(MSG_STR_EMPTY), /* "" makes this the default command */
944 NULL
945 };
946 static elfedit_cmd_optarg_t opt_dump[] = {
947 { MSG_ORIG(MSG_STR_MINUS_ANY),
948 /* MSG_INTL(MSG_OPTDESC_ANY) */
949 ELFEDIT_I18NHDL(MSG_OPTDESC_ANY), 0,
950 STR_OPT_F_ANY, 0 },
951 { ELFEDIT_STDOA_OPT_O, 0,
952 ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
953 { MSG_ORIG(MSG_STR_MINUS_SHNAM),
954 /* MSG_INTL(MSG_OPTDESC_SHNAM) */
955 ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
956 STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
957 { MSG_ORIG(MSG_STR_NAME), 0, 0 },
958 { MSG_ORIG(MSG_STR_MINUS_SHNDX),
959 /* MSG_INTL(MSG_OPTDESC_SHNDX) */
960 ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
961 STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
962 { MSG_ORIG(MSG_STR_INDEX), 0, 0 },
963 { MSG_ORIG(MSG_STR_MINUS_SHTYP),
964 /* MSG_INTL(MSG_OPTDESC_SHTYP) */
965 ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
966 STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
967 { MSG_ORIG(MSG_STR_TYPE), 0, 0 },
968 { MSG_ORIG(MSG_STR_MINUS_STRNDX),
969 /* MSG_INTL(MSG_OPTDESC_STRNDX) */
970 ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
971 STR_OPT_F_STRNDX, 0 },
972 { NULL }
973 };
974 static elfedit_cmd_optarg_t arg_dump[] = {
975 { MSG_ORIG(MSG_STR_STRING),
976 /* MSG_INTL(MSG_A1_STRING) */
977 ELFEDIT_I18NHDL(MSG_A1_STRING),
978 ELFEDIT_CMDOA_F_OPT },
979 { NULL }
980 };
981
982 /* str:set */
983 static const char *name_set[] = {
984 MSG_ORIG(MSG_CMD_SET), NULL };
985 static elfedit_cmd_optarg_t opt_set[] = {
986 { MSG_ORIG(MSG_STR_MINUS_ANY),
987 /* MSG_INTL(MSG_OPTDESC_ANY) */
988 ELFEDIT_I18NHDL(MSG_OPTDESC_ANY), 0,
989 STR_OPT_F_ANY, 0 },
990 { ELFEDIT_STDOA_OPT_O, 0,
991 ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
992 { MSG_ORIG(MSG_STR_MINUS_NOTERM),
993 /* MSG_INTL(MSG_OPTDESC_NOTERM) */
994 ELFEDIT_I18NHDL(MSG_OPTDESC_NOTERM), 0,
995 STR_OPT_F_NOTERM, 0 },
996 { MSG_ORIG(MSG_STR_MINUS_SHNAM),
997 /* MSG_INTL(MSG_OPTDESC_SHNAM) */
998 ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
999 STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
1000 { MSG_ORIG(MSG_STR_NAME), 0, 0 },
1001 { MSG_ORIG(MSG_STR_MINUS_SHNDX),
1002 /* MSG_INTL(MSG_OPTDESC_SHNDX) */
1003 ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
1004 STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
1005 { MSG_ORIG(MSG_STR_INDEX), 0, 0 },
1006 { MSG_ORIG(MSG_STR_MINUS_SHTYP),
1007 /* MSG_INTL(MSG_OPTDESC_SHTYP) */
1008 ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
1009 STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
1010 { MSG_ORIG(MSG_STR_TYPE), 0, 0 },
1011 { MSG_ORIG(MSG_STR_MINUS_STRNDX),
1012 /* MSG_INTL(MSG_OPTDESC_STRNDX) */
1013 ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
1014 STR_OPT_F_STRNDX, 0 },
1015 { NULL }
1016 };
1017 static elfedit_cmd_optarg_t arg_set[] = {
1018 { MSG_ORIG(MSG_STR_STRING),
1019 /* MSG_INTL(MSG_A1_STRING) */
1020 ELFEDIT_I18NHDL(MSG_A1_STRING),
1021 0 },
1022 { MSG_ORIG(MSG_STR_NEWSTRING),
1023 /* MSG_INTL(MSG_A2_NEWSTRING) */
1024 ELFEDIT_I18NHDL(MSG_A2_NEWSTRING),
1025 ELFEDIT_CMDOA_F_OPT },
1026 { NULL }
1027 };
1028
1029 /* str:add */
1030 static const char *name_add[] = {
1031 MSG_ORIG(MSG_CMD_ADD), NULL };
1032 static elfedit_cmd_optarg_t opt_add[] = {
1033 { ELFEDIT_STDOA_OPT_O, 0,
1034 ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
1035 { MSG_ORIG(MSG_STR_MINUS_SHNAM),
1036 /* MSG_INTL(MSG_OPTDESC_SHNAM) */
1037 ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
1038 STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
1039 { MSG_ORIG(MSG_STR_NAME), 0, 0 },
1040 { MSG_ORIG(MSG_STR_MINUS_SHNDX),
1041 /* MSG_INTL(MSG_OPTDESC_SHNDX) */
1042 ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
1043 STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
1044 { MSG_ORIG(MSG_STR_INDEX), 0, 0 },
1045 { MSG_ORIG(MSG_STR_MINUS_SHTYP),
1046 /* MSG_INTL(MSG_OPTDESC_SHTYP) */
1047 ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
1048 STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
1049 { MSG_ORIG(MSG_STR_TYPE), 0, 0 },
1050 { NULL }
1051 };
1052 static elfedit_cmd_optarg_t arg_add[] = {
1053 { MSG_ORIG(MSG_STR_NEWSTRING),
1054 /* MSG_INTL(MSG_A1_NEWSTRING) */
1055 ELFEDIT_I18NHDL(MSG_A1_NEWSTRING),
1056 0 },
1057 { NULL }
1058 };
1059
1060 /* str:zero */
1061 static const char *name_zero[] = {
1062 MSG_ORIG(MSG_CMD_ZERO), NULL };
1063 static elfedit_cmd_optarg_t opt_zero[] = {
1064 { MSG_ORIG(MSG_STR_MINUS_ANY),
1065 /* MSG_INTL(MSG_OPTDESC_ANY) */
1066 ELFEDIT_I18NHDL(MSG_OPTDESC_ANY), 0,
1067 STR_OPT_F_ANY, 0 },
1068 { ELFEDIT_STDOA_OPT_O, 0,
1069 ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
1070 { MSG_ORIG(MSG_STR_MINUS_SHNAM),
1071 /* MSG_INTL(MSG_OPTDESC_SHNAM) */
1072 ELFEDIT_I18NHDL(MSG_OPTDESC_SHNAM), ELFEDIT_CMDOA_F_VALUE,
1073 STR_OPT_F_SHNAME, STR_OPT_F_SHNDX | STR_OPT_F_SHTYP },
1074 { MSG_ORIG(MSG_STR_NAME), 0, 0 },
1075 { MSG_ORIG(MSG_STR_MINUS_SHNDX),
1076 /* MSG_INTL(MSG_OPTDESC_SHNDX) */
1077 ELFEDIT_I18NHDL(MSG_OPTDESC_SHNDX), ELFEDIT_CMDOA_F_VALUE,
1078 STR_OPT_F_SHNDX, STR_OPT_F_SHNAME | STR_OPT_F_SHTYP },
1079 { MSG_ORIG(MSG_STR_INDEX), 0, 0 },
1080 { MSG_ORIG(MSG_STR_MINUS_SHTYP),
1081 /* MSG_INTL(MSG_OPTDESC_SHTYP) */
1082 ELFEDIT_I18NHDL(MSG_OPTDESC_SHTYP), ELFEDIT_CMDOA_F_VALUE,
1083 STR_OPT_F_SHTYP, STR_OPT_F_SHNAME | STR_OPT_F_SHNDX },
1084 { MSG_ORIG(MSG_STR_TYPE), 0, 0 },
1085 { MSG_ORIG(MSG_STR_MINUS_STRNDX),
1086 /* MSG_INTL(MSG_OPTDESC_STRNDX) */
1087 ELFEDIT_I18NHDL(MSG_OPTDESC_STRNDX), 0,
1088 STR_OPT_F_STRNDX, 0 },
1089 { MSG_ORIG(MSG_STR_MINUS_END),
1090 /* MSG_INTL(MSG_OPTDESC_END) */
1091 ELFEDIT_I18NHDL(MSG_OPTDESC_END), 0,
1092 STR_OPT_F_END, 0 },
1093 { NULL }
1094 };
1095 static elfedit_cmd_optarg_t arg_zero[] = {
1096 { MSG_ORIG(MSG_STR_STRING),
1097 /* MSG_INTL(MSG_A1_STRING) */
1098 ELFEDIT_I18NHDL(MSG_A1_STRING),
1099 0 },
1100 { MSG_ORIG(MSG_STR_COUNT),
1101 /* MSG_INTL(MSG_A2_COUNT) */
1102 ELFEDIT_I18NHDL(MSG_A2_COUNT),
1103 ELFEDIT_CMDOA_F_OPT },
1104 { NULL }
1105 };
1106
1107
1108 static elfedit_cmd_t cmds[] = {
1109 /* str:dump */
1110 { cmd_dump, cpl_sec_str, name_dump,
1111 /* MSG_INTL(MSG_DESC_DUMP) */
1112 ELFEDIT_I18NHDL(MSG_DESC_DUMP),
1113 /* MSG_INTL(MSG_HELP_DUMP) */
1114 ELFEDIT_I18NHDL(MSG_HELP_DUMP),
1115 opt_dump, arg_dump },
1116
1117 /* str:set */
1118 { cmd_set, cpl_sec_str, name_set,
1119 /* MSG_INTL(MSG_DESC_SET) */
1120 ELFEDIT_I18NHDL(MSG_DESC_SET),
1121 /* MSG_INTL(MSG_HELP_SET) */
1122 ELFEDIT_I18NHDL(MSG_HELP_SET),
1123 opt_set, arg_set },
1124
1125 /* str:add */
1126 { cmd_add, cpl_sh_opt, name_add,
1127 /* MSG_INTL(MSG_DESC_ADD) */
1128 ELFEDIT_I18NHDL(MSG_DESC_ADD),
1129 /* MSG_INTL(MSG_HELP_ADD) */
1130 ELFEDIT_I18NHDL(MSG_HELP_ADD),
1131 opt_add, arg_add },
1132
1133 /* str:zero */
1134 { cmd_zero, cpl_sec_str, name_zero,
1135 /* MSG_INTL(MSG_DESC_ZERO) */
1136 ELFEDIT_I18NHDL(MSG_DESC_ZERO),
1137 /* MSG_INTL(MSG_HELP_ZERO) */
1138 ELFEDIT_I18NHDL(MSG_HELP_ZERO),
1139 opt_zero, arg_zero },
1140
1141 { NULL }
1142 };
1143
1144 static elfedit_module_t module = {
1145 ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_NAME),
1146 /* MSG_INTL(MSG_MOD_DESC) */
1147 ELFEDIT_I18NHDL(MSG_MOD_DESC),
1148 cmds, mod_i18nhdl_to_str };
1149
1150 return (&module);
1151 }
1152