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 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <stdarg.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <signal.h>
35 #include <dirent.h>
36 #include <libelf.h>
37 #include <gelf.h>
38 #include <conv.h>
39 #include <dlfcn.h>
40 #include <link.h>
41 #include <stdarg.h>
42 #include <libgen.h>
43 #include <libintl.h>
44 #include <locale.h>
45 #include <unistd.h>
46 #include <errno.h>
47 #include <ctype.h>
48 #include <limits.h>
49 #include <strings.h>
50 #include <sgs.h>
51 #include "msg.h"
52 #include "_elfedit.h"
53 #include <debug.h> /* liblddb */
54
55
56
57 /*
58 * Column at which elfedit_format_command_usage() will wrap the
59 * generated usage string if the wrap argument is True (1).
60 */
61 #define USAGE_WRAP_COL 55
62
63
64
65
66 /*
67 * Type used to represent a string buffer that can grow as needed
68 * to hold strings of arbitrary length. The user should declare
69 * variables of this type sa static. The strbuf_ensure_size() function
70 * is used to ensure that it has a minimum desired size.
71 */
72 typedef struct {
73 char *buf; /* String buffer */
74 size_t n; /* Size of buffer */
75 } STRBUF;
76
77
78
79
80 /*
81 * Types used by tokenize_user_cmd() to represent the result of
82 * spliting a user command into individual tokens.
83 */
84 typedef struct {
85 char *tok_str; /* Token string */
86 size_t tok_len; /* strlen(str) */
87 size_t tok_line_off; /* Token offset in original string */
88 } TOK_ELT;
89 typedef struct {
90 size_t tokst_cmd_len; /* Length of original user command, without */
91 /* newline or NULL termination chars */
92 size_t tokst_str_size; /* Space needed to hold all the resulting */
93 /* tokens, including terminating NULL */
94 TOK_ELT *tokst_buf; /* The array of tokens */
95 size_t tokst_cnt; /* # of tokens in array */
96 size_t tokst_bufsize; /* capacity of array */
97 } TOK_STATE;
98
99
100
101
102 /* State block used by gettok_init() and gettok() */
103 typedef struct {
104 const char *gtok_buf; /* Addr of buffer containing string */
105 char *gtok_cur_buf; /* Addr withing buffer for next token */
106 int gtok_inc_null_final; /* True if final NULL token used */
107 int gtok_null_seen; /* True when NULL byte seen */
108 TOK_ELT gtok_last_token; /* Last token parsed */
109
110 } GETTOK_STATE;
111
112
113
114
115 /*
116 * The elfedit_cpl_*() functions are used for command line completion.
117 * Currently this uses the tecla library, but to allow for changing the
118 * library used, we hide all tecla interfaces from our modules. Instead,
119 * cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the
120 * address of that struct as an opaque handle to the modules. Since the
121 * pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change
122 * as necessary.
123 */
124 typedef struct {
125 WordCompletion *ecpl_cpl; /* tecla handle */
126 const char *ecpl_line; /* raw input line */
127 int ecpl_word_start; /* start offset within line */
128 int ecpl_word_end; /* offset just past token */
129 /*
130 * ecpl_add_mod_colon is a secret handshake between
131 * elfedit_cpl_command() and elfedit_cpl_add_match(). It adds
132 * ':' to end of matched modules.
133 */
134 int ecpl_add_mod_colon;
135 const char *ecpl_token_str; /* token being completed */
136 size_t ecpl_token_len; /* strlen(ecpl_token_str) */
137 } ELFEDIT_CPL_STATE;
138
139
140
141
142 /* This structure maintains elfedit global state */
143 STATE_T state;
144
145
146
147 /*
148 * Define a pair of static global variables that contain the
149 * ISA strings that correspond to %i and %I tokens in module search
150 * paths.
151 *
152 * isa_i_str - The ISA string for the currently running program
153 * isa_I_str - For 64-bit programs, the same as isa_i_str. For
154 * 32-bit programs, an empty string.
155 */
156 #ifdef __sparc
157 #ifdef __sparcv9
158 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_64);
159 static const char *isa_I_str = MSG_ORIG(MSG_ISA_SPARC_64);
160 #else
161 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_32);
162 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
163 #endif
164 #endif
165
166 #ifdef __i386
167 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_32);
168 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY);
169 #endif
170 #ifdef __amd64
171 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_64);
172 static const char *isa_I_str = MSG_ORIG(MSG_ISA_X86_64);
173 #endif
174
175
176
177 /* Forward declarations */
178 static void free_user_cmds(void);
179 static void elfedit_pager_cleanup(void);
180
181
182
183 /*
184 * We supply this function for the msg module
185 */
186 const char *
_elfedit_msg(Msg mid)187 _elfedit_msg(Msg mid)
188 {
189 return (gettext(MSG_ORIG(mid)));
190 }
191
192
193 /*
194 * Copy at most min(cpsize, dstsize-1) bytes from src into dst,
195 * truncating src if necessary. The result is always null-terminated.
196 *
197 * entry:
198 * dst - Destination buffer
199 * src - Source string
200 * dstsize - sizeof(dst)
201 *
202 * note:
203 * This is similar to strncpy(), but with two modifications:
204 * 1) You specify the number of characters to copy, not just
205 * the size of the destination. Hence, you can copy non-NULL
206 * terminated strings.
207 * 2) The destination is guaranteed to be NULL terminated. strncpy()
208 * does not terminate a completely full buffer.
209 */
210 static void
elfedit_strnbcpy(char * dst,const char * src,size_t cpsize,size_t dstsize)211 elfedit_strnbcpy(char *dst, const char *src, size_t cpsize, size_t dstsize)
212 {
213 if (cpsize >= dstsize)
214 cpsize = dstsize - 1;
215 if (cpsize > 0)
216 (void) strncpy(dst, src, cpsize + 1);
217 dst[cpsize] = '\0';
218 }
219
220
221 /*
222 * Calls exit() on behalf of elfedit.
223 */
224 void
elfedit_exit(int status)225 elfedit_exit(int status)
226 {
227 if (state.file.present) {
228 /* Exiting with unflushed changes pending? Issue debug notice */
229 if (state.file.dirty)
230 elfedit_msg(ELFEDIT_MSG_DEBUG,
231 MSG_INTL(MSG_DEBUG_DIRTYEXIT));
232
233 /*
234 * If the edit file is marked for unlink on exit, then
235 * take care of it here.
236 */
237 if (state.file.unlink_on_exit) {
238 elfedit_msg(ELFEDIT_MSG_DEBUG,
239 MSG_INTL(MSG_DEBUG_UNLINKFILE),
240 state.file.outfile);
241 (void) unlink(state.file.outfile);
242 }
243 }
244
245 exit(status);
246 }
247
248
249 /*
250 * Standard message function for elfedit. All user visible
251 * output, for error or informational reasons, should go through
252 * this function.
253 *
254 * entry:
255 * type - Type of message. One of the ELFEDIT_MSG_* values.
256 * format, ... - As per the printf() family
257 *
258 * exit:
259 * The desired message has been output. For informational
260 * messages, control returns to the caller. For errors,
261 * this routine will terminate execution or strip the execution
262 * stack and return control directly to the outer control loop.
263 * In either case, the caller will not receive control.
264 */
265 /*PRINTFLIKE2*/
266 void
elfedit_msg(elfedit_msg_t type,const char * format,...)267 elfedit_msg(elfedit_msg_t type, const char *format, ...)
268 {
269 typedef enum { /* What to do after finished */
270 DISP_RET = 0, /* Return to caller */
271 DISP_JMP = 1, /* if (interactive) longjmp else exit */
272 DISP_EXIT = 2 /* exit under all circumstances */
273 } DISP;
274
275 va_list args;
276 FILE *stream = stderr;
277 DISP disp = DISP_RET;
278 int do_output = 1;
279 int need_prefix = 1;
280
281 va_start(args, format);
282
283 switch (type) {
284 case ELFEDIT_MSG_ERR:
285 case ELFEDIT_MSG_CMDUSAGE:
286 disp = DISP_JMP;
287 break;
288 case ELFEDIT_MSG_FATAL:
289 disp = DISP_EXIT;
290 break;
291 case ELFEDIT_MSG_USAGE:
292 need_prefix = 0;
293 break;
294 case ELFEDIT_MSG_DEBUG:
295 if (!(state.flags & ELFEDIT_F_DEBUG))
296 return;
297 stream = stdout;
298 break;
299 case ELFEDIT_MSG_QUIET:
300 do_output = 0;
301 disp = DISP_JMP;
302 break;
303 }
304
305
306 /*
307 * If there is a pager process running, we are returning to the
308 * caller, and the output is going to stdout, then let the
309 * pager handle it instead of writing it directly from this process.
310 * That way, the output gets paged along with everything else.
311 *
312 * If there is a pager process running, and we are not returning
313 * to the caller, then end the pager process now, before we generate
314 * any new output. This allows for any text buffered in the pager
315 * pipe to be output before the new stuff.
316 */
317 if (state.pager.fptr != NULL) {
318 if (disp == DISP_RET) {
319 if (stream == stdout)
320 stream = state.pager.fptr;
321 } else {
322 elfedit_pager_cleanup();
323 }
324 }
325
326 /*
327 * If this message is coming from within the libtecla command
328 * completion code, call gl_normal_io() to give the library notice.
329 * That function sets the tty back to cooked mode and advances
330 * the cursor to the beginning of the next line so that our output
331 * will appear properly. When we return to the command completion code,
332 * tecla will re-enter raw mode and redraw the current command line.
333 */
334 if (state.input.in_tecla)
335 (void) gl_normal_io(state.input.gl);
336
337 if (do_output) {
338 if (need_prefix)
339 (void) fprintf(stream, MSG_ORIG(MSG_STR_ELFEDIT));
340 (void) vfprintf(stream, format, args);
341 (void) fflush(stream);
342 }
343 va_end(args);
344
345 /*
346 * If this is an error, then we do not return to the caller.
347 * The action taken depends on whether the outer loop has registered
348 * a jump buffer for us or not.
349 */
350 if (disp != DISP_RET) {
351 if (state.msg_jbuf.active && (disp == DISP_JMP)) {
352 /* Free the user command list */
353 free_user_cmds();
354
355 /* Clean up to reflect effect of non-local goto */
356 state.input.in_tecla = FALSE;
357
358 /* Jump to the outer loop to resume */
359 siglongjmp(state.msg_jbuf.env, 1);
360 } else {
361 elfedit_exit(1);
362 }
363 }
364 }
365
366
367 /*
368 * Wrapper on elfedit_msg() that issues an error that results from
369 * a call to libelf.
370 *
371 * entry:
372 * file - Name of ELF object
373 * libelf_rtn_name - Name of routine that was called
374 *
375 * exit:
376 * An error has been issued that shows the routine called
377 * and the libelf error string for it from elf_errmsg().
378 * This routine does not return to the caller.
379 */
380 void
elfedit_elferr(const char * file,const char * libelf_rtn_name)381 elfedit_elferr(const char *file, const char *libelf_rtn_name)
382 {
383 const char *errstr = elf_errmsg(elf_errno());
384
385 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF), file,
386 libelf_rtn_name, errstr ? errstr : MSG_INTL(MSG_FMT_UNKNOWN));
387 }
388
389
390 /*
391 * Start an output pager process for elfedit_printf()/elfedit_write() to use.
392 *
393 * note:
394 * If this elfedit session is not interactive, then no pager is
395 * started. Paging is only intended for interactive use. The caller
396 * is not supposed to worry about this point, but simply to use
397 * this function to flag situations in which paging might be needed.
398 */
399 void
elfedit_pager_init(void)400 elfedit_pager_init(void)
401 {
402 const char *errstr;
403 const char *cmd;
404 int err;
405
406 /*
407 * If there is no pager process running, start one.
408 * Only do this for interactive sessions --- elfedit_pager()
409 * won't use a pager in batch mode.
410 */
411 if (state.msg_jbuf.active && state.input.full_tty &&
412 (state.pager.fptr == NULL)) {
413 /*
414 * If the user has the PAGER environment variable set,
415 * then we will use that program. Otherwise we default
416 * to /bin/more.
417 */
418 cmd = getenv(MSG_ORIG(MSG_STR_PAGER));
419 if ((cmd == NULL) || (*cmd == '\0'))
420 cmd = MSG_ORIG(MSG_STR_BINMORE);
421
422 /*
423 * The popen() manpage says that on failure, it "may set errno",
424 * which is somewhat ambiguous. We explicitly zero it here, and
425 * assume that any change is due to popen() failing.
426 */
427 errno = 0;
428 state.pager.fptr = popen(cmd, MSG_ORIG(MSG_STR_W));
429 if (state.pager.fptr == NULL) {
430 err = errno;
431 errstr = (err == 0) ? MSG_INTL(MSG_ERR_UNKNOWNSYSERR) :
432 strerror(err);
433 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTEXEC),
434 MSG_ORIG(MSG_STR_ELFEDIT), cmd, errstr);
435 }
436 }
437 }
438
439
440 /*
441 * If there is a pager process present, close it out.
442 *
443 * note:
444 * This function is called from within elfedit_msg(), and as
445 * such, must not use elfedit_msg() to report errors. Furthermore,
446 * any such errors are not a sufficient reason to terminate the process
447 * or to longjmp(). This is a rare case where errors are written
448 * directly to stderr.
449 */
450 static void
elfedit_pager_cleanup(void)451 elfedit_pager_cleanup(void)
452 {
453 if (state.pager.fptr != NULL) {
454 if (pclose(state.pager.fptr) == -1)
455 (void) fprintf(stderr, MSG_INTL(MSG_ERR_PAGERFINI));
456
457 state.pager.fptr = NULL;
458 }
459 }
460
461
462 /*
463 * Print general formtted text for the user, using printf()-style
464 * formatting. Uses the pager process if one has been started, or
465 * stdout otherwise.
466 */
467 void
elfedit_printf(const char * format,...)468 elfedit_printf(const char *format, ...)
469 {
470 va_list args;
471 int err;
472 FILE *fptr;
473 int pager;
474 int broken_pipe = 0;
475
476 /*
477 * If there is a pager process, then use it. Otherwise write
478 * directly to stdout.
479 */
480 pager = (state.pager.fptr != NULL);
481 fptr = pager ? state.pager.fptr : stdout;
482
483 va_start(args, format);
484 errno = 0;
485 err = vfprintf(fptr, format, args);
486
487 /* Did we fail because a child pager process has exited? */
488 broken_pipe = pager && (err < 0) && (errno == EPIPE);
489
490 va_end(args);
491
492 /*
493 * On error, we simply issue the error without cleaning up
494 * the pager process. The message code handles that as a standard
495 * part of error processing.
496 *
497 * We handle failure due to an exited pager process differently
498 * than a normal error, because it is usually due to the user
499 * intentionally telling it to.
500 */
501 if (err < 0) {
502 if (broken_pipe)
503 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
504 else
505 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
506 }
507 }
508
509
510 /*
511 * Some our modules use liblddb routines to format ELF output.
512 * In order to ensure that such output is sent to the pager pipe
513 * when there is one, and stdout otherwise, we redefine the dbg_print()
514 * function here.
515 *
516 * This item should be defined NODIRECT.
517 */
518 /* PRINTFLIKE2 */
519 void
dbg_print(Lm_list * lml,const char * format,...)520 dbg_print(Lm_list *lml, const char *format, ...)
521 {
522 va_list ap;
523 int err;
524 FILE *fptr;
525 int pager;
526 int broken_pipe = 0;
527
528 #if defined(lint)
529 /*
530 * The lml argument is only meaningful for diagnostics sent to ld.so.1.
531 * Supress the lint error by making a dummy assignment.
532 */
533 lml = 0;
534 #endif
535
536 /*
537 * If there is a pager process, then use it. Otherwise write
538 * directly to stdout.
539 */
540 pager = (state.pager.fptr != NULL);
541 fptr = pager ? state.pager.fptr : stdout;
542
543 va_start(ap, format);
544 errno = 0;
545 err = vfprintf(fptr, format, ap);
546 if (err >= 0)
547 err = fprintf(fptr, MSG_ORIG(MSG_STR_NL));
548
549 /* Did we fail because a child pager process has exited? */
550 broken_pipe = (err < 0) && pager && (errno == EPIPE);
551
552 va_end(ap);
553
554 /*
555 * On error, we simply issue the error without cleaning up
556 * the pager process. The message code handles that as a standard
557 * part of error processing.
558 *
559 * We handle failure due to an exited pager process differently
560 * than a normal error, because it is usually due to the user
561 * intentionally telling it to.
562 */
563 if (err < 0) {
564 if (broken_pipe)
565 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL));
566 else
567 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF));
568 }
569 }
570
571
572 /*
573 * Write raw bytes of text in a manner similar to fwrite().
574 * Uses the pager process if one has been started, or
575 * stdout otherwise.
576 */
577 void
elfedit_write(const void * ptr,size_t size)578 elfedit_write(const void *ptr, size_t size)
579 {
580 FILE *fptr;
581 int err;
582
583 /*
584 * If there is a pager process, then use it. Otherwise write
585 * directly to stdout.
586 */
587 fptr = (state.pager.fptr == NULL) ? stdout : state.pager.fptr;
588
589 if (fwrite(ptr, 1, size, fptr) != size) {
590 err = errno;
591 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FWRITE),
592 strerror(err));
593 }
594 }
595
596
597 /*
598 * Convert the NULL terminated string to the form used by the C
599 * language to represent literal strings. See conv_str_to_c_literal()
600 * for details.
601 *
602 * This routine differs from conv_str_to_c_literal() in two ways:
603 * 1) String is NULL terminated instead of counted
604 * 2) Signature of outfunc
605 *
606 * entry:
607 * str - String to be processed
608 * outfunc - Function to be called to move output characters. Note
609 * that this function has the same signature as elfedit_write(),
610 * and that function can be used to write the characters to
611 * the output.
612 *
613 * exit:
614 * The string has been processed, with the resulting data passed
615 * to outfunc for processing.
616 */
617 static void
elfedit_str_to_c_literal_cb(const void * ptr,size_t size,void * uvalue)618 elfedit_str_to_c_literal_cb(const void *ptr, size_t size, void *uvalue)
619 {
620 elfedit_write_func_t *outfunc = (elfedit_write_func_t *)uvalue;
621
622 (* outfunc)(ptr, size);
623
624 }
625 void
elfedit_str_to_c_literal(const char * str,elfedit_write_func_t * outfunc)626 elfedit_str_to_c_literal(const char *str, elfedit_write_func_t *outfunc)
627 {
628 conv_str_to_c_literal(str, strlen(str),
629 elfedit_str_to_c_literal_cb, (void *) outfunc);
630 }
631
632
633 /*
634 * Wrappers on malloc() and realloc() that check the result for success
635 * and issue an error if not. The caller can use the result of these
636 * functions without checking for a NULL pointer, as we do not return to
637 * the caller in the failure case.
638 */
639 void *
elfedit_malloc(const char * item_name,size_t size)640 elfedit_malloc(const char *item_name, size_t size)
641 {
642 void *m;
643
644 m = malloc(size);
645 if (m == NULL) {
646 int err = errno;
647 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
648 item_name, strerror(err));
649 }
650
651 return (m);
652 }
653
654 void *
elfedit_realloc(const char * item_name,void * ptr,size_t size)655 elfedit_realloc(const char *item_name, void *ptr, size_t size)
656 {
657 void *m;
658
659 m = realloc(ptr, size);
660 if (m == NULL) {
661 int err = errno;
662 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC),
663 item_name, strerror(err));
664 }
665
666 return (m);
667 }
668
669
670 /*
671 * Ensure that the given buffer has room for n bytes of data.
672 */
673 static void
strbuf_ensure_size(STRBUF * str,size_t size)674 strbuf_ensure_size(STRBUF *str, size_t size)
675 {
676 #define INITIAL_STR_ALLOC 128
677
678 size_t n;
679
680 n = (str->n == 0) ? INITIAL_STR_ALLOC : str->n;
681 while (size > n) /* Double buffer until string fits */
682 n *= 2;
683 if (n != str->n) { /* Alloc new string buffer if needed */
684 str->buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_UCMDSTR),
685 str->buf, n);
686 str->n = n;
687 }
688
689 #undef INITIAL_STR_ALLOC
690 }
691
692
693 /*
694 * Extract the argument/option information for the next item referenced
695 * by optarg, and advance the pointer to the next item.
696 *
697 * entry:
698 * optarg - Address of pointer to argument or option array
699 * item - Struct to be filled in.
700 *
701 * exit:
702 * The item block has been filled in with the information for
703 * the next item in the optarg array. *optarg has been advanced
704 * to the next item.
705 */
706 void
elfedit_next_optarg(elfedit_cmd_optarg_t ** optarg,elfedit_optarg_item_t * item)707 elfedit_next_optarg(elfedit_cmd_optarg_t **optarg, elfedit_optarg_item_t *item)
708 {
709 /*
710 * Array of inheritable options/arguments. Indexed by one less
711 * than the corresponding ELFEDIT_STDOA_ value.
712 */
713 static const elfedit_optarg_item_t stdoa[] = {
714 /* ELFEDIT_STDOA_O */
715 { MSG_ORIG(MSG_STR_MINUS_O), MSG_ORIG(MSG_STR_OUTSTYLE),
716 /* MSG_INTL(MSG_STDOA_OPTDESC_O) */
717 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_O,
718 ELFEDIT_CMDOA_F_VALUE },
719
720 /* ELFEDIT_STDOA_AND */
721 { MSG_ORIG(MSG_STR_MINUS_AND), NULL,
722 /* MSG_INTL(MSG_STDOA_OPTDESC_AND) */
723 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_AND, 0 },
724
725 /* ELFEDIT_STDOA_CMP */
726 { MSG_ORIG(MSG_STR_MINUS_CMP), NULL,
727 /* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */
728 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_CMP, 0 },
729
730 /* ELFEDIT_STDOA_OR */
731 { MSG_ORIG(MSG_STR_MINUS_OR), NULL,
732 /* MSG_INTL(MSG_STDOA_OPTDESC_OR) */
733 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_OR, 0 },
734 };
735
736 elfedit_cmd_optarg_t *oa;
737
738
739 /* Grab first item, advance the callers pointer over it */
740 oa = (*optarg)++;
741
742 if (oa->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
743 /* Values are pre-chewed in the stdoa array above */
744 *item = stdoa[((uintptr_t)oa->oa_name) - 1];
745
746 /*
747 * Set the inherited flag so that elfedit_optarg_helpstr()
748 * can tell who is responsible for translating the help string.
749 */
750 item->oai_flags |= ELFEDIT_CMDOA_F_INHERIT;
751 } else { /* Non-inherited item */
752 item->oai_name = oa->oa_name;
753 if ((oa->oa_flags & ELFEDIT_CMDOA_F_VALUE) != 0) {
754 item->oai_vname = oa[1].oa_name;
755
756 /* Advance users pointer past value element */
757 (*optarg)++;
758 } else {
759 item->oai_vname = NULL;
760 }
761 item->oai_help = oa->oa_help;
762 item->oai_flags = oa->oa_flags;
763 }
764
765 /*
766 * The module determines the idmask and excmask fields whether
767 * or not inheritance is in play.
768 */
769 item->oai_idmask = oa->oa_idmask;
770 item->oai_excmask = oa->oa_excmask;
771 }
772
773
774
775 /*
776 * Return the help string for an option/argument item, as returned
777 * by elfedit_next_optarg(). This routine handles the details of
778 * knowing whether the string is provided by elfedit itself (inherited),
779 * or needs to be translated by the module.
780 */
781 const char *
elfedit_optarg_helpstr(elfeditGC_module_t * mod,elfedit_optarg_item_t * item)782 elfedit_optarg_helpstr(elfeditGC_module_t *mod, elfedit_optarg_item_t *item)
783 {
784 /*
785 * The help string from an inherited item comes right out
786 * of the main elfedit string table.
787 */
788 if (item->oai_flags & ELFEDIT_CMDOA_F_INHERIT)
789 return (MSG_INTL((Msg) item->oai_help));
790
791 /*
792 * If the string is defined by the module, then we need to
793 * have the module translate it for us.
794 */
795 return ((* mod->mod_i18nhdl_to_str)(item->oai_help));
796 }
797
798
799
800 /*
801 * Used by usage_optarg() to insert a character into the output buffer,
802 * advancing the buffer pointer and current column, and reducing the
803 * amount of remaining space.
804 */
805 static void
usage_optarg_insert_ch(int ch,char ** cur,size_t * n,size_t * cur_col)806 usage_optarg_insert_ch(int ch, char **cur, size_t *n, size_t *cur_col)
807 {
808
809 *(*cur)++ = ch;
810 **cur = '\0';
811 (*n)--;
812 (*cur_col)++;
813 }
814
815 /*
816 * Used by usage_optarg() to insert a string into the output
817 * buffer, advancing the buffer pointer and current column, and reducing
818 * the amount of remaining space.
819 */
820 static void
usage_optarg_insert_str(char ** cur,size_t * n,size_t * cur_col,const char * format,...)821 usage_optarg_insert_str(char **cur, size_t *n, size_t *cur_col,
822 const char *format, ...)
823 {
824 size_t len;
825 va_list args;
826
827 va_start(args, format);
828 len = vsnprintf(*cur, *n, format, args);
829 va_end(args);
830
831 *cur += len;
832 *n -= len;
833 *cur_col += len;
834 }
835 /*
836 * Used by usage_optarg() to insert an optarg item string into the output
837 * buffer, advancing the buffer pointer and current column, and reducing
838 * the amount of remaining space.
839 */
840 static void
usage_optarg_insert_item(elfedit_optarg_item_t * item,char ** cur,size_t * n,size_t * cur_col)841 usage_optarg_insert_item(elfedit_optarg_item_t *item, char **cur,
842 size_t *n, size_t *cur_col)
843 {
844 size_t len;
845
846 if (item->oai_flags & ELFEDIT_CMDOA_F_VALUE) {
847 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG2),
848 item->oai_name, item->oai_vname);
849 } else {
850 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG),
851 item->oai_name);
852 }
853 *cur += len;
854 *n -= len;
855 *cur_col += len;
856 }
857
858
859
860 /*
861 * Write the options/arguments to the usage string.
862 *
863 * entry:
864 * main_buf_n - Size of main buffer from which buf and buf_n are
865 * allocated.
866 * buf - Address of pointer to where next item is to be placed.
867 * buf_n - Address of count of remaining bytes in buffer
868 * buf_cur_col - Address of current output column for current line
869 * of generated string.
870 * optarg - Options list
871 * isopt - True if these are options, false for arguments.
872 * wrap_str - String to indent wrapped lines. If NULL, lines
873 * are not wrapped
874 */
875 static void
usage_optarg(size_t main_buf_n,char ** buf,size_t * buf_n,size_t * buf_cur_col,elfedit_cmd_optarg_t * optarg,int isopt,const char * wrap_str)876 usage_optarg(size_t main_buf_n, char **buf, size_t *buf_n, size_t *buf_cur_col,
877 elfedit_cmd_optarg_t *optarg, int isopt, const char *wrap_str)
878 {
879 /*
880 * An option can be combined into a simple format if it lacks
881 * these flags and is only one character in length.
882 */
883 static const elfedit_cmd_oa_flag_t exflags =
884 (ELFEDIT_CMDOA_F_VALUE | ELFEDIT_CMDOA_F_MULT);
885
886 /*
887 * A static buffer, which is grown as needed to accomodate
888 * the maximum usage string seen.
889 */
890 static STRBUF simple_str;
891
892 char *cur = *buf;
893 size_t n = *buf_n;
894 size_t cur_col = *buf_cur_col;
895 int len;
896 int use_simple = 0;
897 elfedit_optarg_item_t item;
898 elfedit_cmd_oa_mask_t optmask = 0;
899 int use_bkt;
900
901 /*
902 * If processing options, pull the 1-character ones that don't have
903 * an associated value and don't have any mutual exclusion issues into
904 * a single combination string to go at the beginning of the usage.
905 */
906 if (isopt) {
907 elfedit_cmd_optarg_t *tmp_optarg = optarg;
908 char *s;
909
910 /*
911 * The simple string is guaranteed to fit in the same
912 * amount of space reserved for the main buffer.
913 */
914 strbuf_ensure_size(&simple_str, main_buf_n);
915 s = simple_str.buf;
916 *s++ = ' ';
917 *s++ = '[';
918 *s++ = '-';
919 while (tmp_optarg->oa_name != NULL) {
920 elfedit_next_optarg(&tmp_optarg, &item);
921 if (((item.oai_flags & exflags) == 0) &&
922 (item.oai_name[2] == '\0') &&
923 (item.oai_excmask == 0)) {
924 optmask |= item.oai_idmask;
925 *s++ = item.oai_name[1];
926 }
927 }
928
929 /*
930 * If we found more than one, then finish the string and
931 * add it. Don't do this for a single option, because
932 * it looks better in that case if the option shows up
933 * in alphabetical order rather than being hoisted.
934 */
935 use_simple = (s > (simple_str.buf + 4));
936 if (use_simple) {
937 *s++ = ']';
938 *s++ = '\0';
939 usage_optarg_insert_str(&cur, &n, &cur_col,
940 MSG_ORIG(MSG_STR_HLPOPTARG), simple_str.buf);
941 } else {
942 /* Not using it, so reset the cumulative options mask */
943 optmask = 0;
944 }
945 }
946
947 while (optarg->oa_name != NULL) {
948 elfedit_next_optarg(&optarg, &item);
949
950 if (isopt) {
951 /*
952 * If this is an option that was pulled into the
953 * combination string above, then skip over it.
954 */
955 if (use_simple && ((item.oai_flags & exflags) == 0) &&
956 (item.oai_name[2] == '\0') &&
957 (item.oai_excmask == 0))
958 continue;
959
960 /*
961 * If this is a mutual exclusion option that was
962 * picked up out of order by a previous iteration
963 * of this loop, then skip over it.
964 */
965 if ((optmask & item.oai_idmask) != 0)
966 continue;
967
968 /* Add this item to the accumulating options mask */
969 optmask |= item.oai_idmask;
970 }
971
972 /* Wrap line, or insert blank separator */
973 if ((wrap_str != NULL) && (cur_col > USAGE_WRAP_COL)) {
974 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_WRAPUSAGE),
975 wrap_str);
976 cur += len;
977 n -= len;
978 cur_col = len - 1; /* Don't count the newline */
979 } else {
980 usage_optarg_insert_ch(' ', &cur, &n, &cur_col);
981 }
982
983 use_bkt = (item.oai_flags & ELFEDIT_CMDOA_F_OPT) || isopt;
984 if (use_bkt)
985 usage_optarg_insert_ch('[', &cur, &n, &cur_col);
986
987 /* Add the item to the buffer */
988 usage_optarg_insert_item(&item, &cur, &n, &cur_col);
989
990 /*
991 * If this item has a non-zero mutual exclusion mask,
992 * then look for the other items and display them all
993 * together with alternation (|). Note that plain arguments
994 * cannot have a non-0 exclusion mask, so this is
995 * effectively options-only (isopt != 0).
996 */
997 if (item.oai_excmask != 0) {
998 elfedit_cmd_optarg_t *tmp_optarg = optarg;
999 elfedit_optarg_item_t tmp_item;
1000
1001 /*
1002 * When showing alternation, elipses for multiple
1003 * copies need to appear inside the [] brackets.
1004 */
1005 if (item.oai_flags & ELFEDIT_CMDOA_F_MULT)
1006 usage_optarg_insert_str(&cur, &n, &cur_col,
1007 MSG_ORIG(MSG_STR_ELIPSES));
1008
1009
1010 while (tmp_optarg->oa_name != NULL) {
1011 elfedit_next_optarg(&tmp_optarg, &tmp_item);
1012 if ((item.oai_excmask & tmp_item.oai_idmask) ==
1013 0)
1014 continue;
1015 usage_optarg_insert_str(&cur, &n, &cur_col,
1016 MSG_ORIG(MSG_STR_SP_BAR_SP));
1017 usage_optarg_insert_item(&tmp_item,
1018 &cur, &n, &cur_col);
1019
1020 /*
1021 * Add it to the mask of seen options.
1022 * This will keep us from showing it twice.
1023 */
1024 optmask |= tmp_item.oai_idmask;
1025 }
1026 }
1027 if (use_bkt)
1028 usage_optarg_insert_ch(']', &cur, &n, &cur_col);
1029
1030 /*
1031 * If alternation was not shown above (non-zero exclusion mask)
1032 * then the elipses for multiple copies are shown outside
1033 * any [] brackets.
1034 */
1035 if ((item.oai_excmask == 0) &&
1036 (item.oai_flags & ELFEDIT_CMDOA_F_MULT))
1037 usage_optarg_insert_str(&cur, &n, &cur_col,
1038 MSG_ORIG(MSG_STR_ELIPSES));
1039
1040 }
1041
1042 *buf = cur;
1043 *buf_n = n;
1044 *buf_cur_col = cur_col;
1045 }
1046
1047
1048
1049 /*
1050 * Format the usage string for a command into a static buffer and
1051 * return the pointer to the user. The resultant string is valid
1052 * until the next call to this routine, and which point it
1053 * will be overwritten or the memory is freed.
1054 *
1055 * entry:
1056 * mod, cmd - Module and command definitions for command to be described
1057 * wrap_str - NULL, or string to be used to indent when
1058 * lines are wrapped. If NULL, no wrapping is done, and
1059 * all output is on a single line.
1060 * cur_col - Starting column at which the string will be displayed.
1061 * Ignored if wrap_str is NULL.
1062 */
1063 const char *
elfedit_format_command_usage(elfeditGC_module_t * mod,elfeditGC_cmd_t * cmd,const char * wrap_str,size_t cur_col)1064 elfedit_format_command_usage(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd,
1065 const char *wrap_str, size_t cur_col)
1066 {
1067
1068 /*
1069 * A static buffer, which is grown as needed to accomodate
1070 * the maximum usage string seen.
1071 */
1072 static STRBUF str;
1073
1074 elfedit_cmd_optarg_t *optarg;
1075 size_t len, n, elipses_len;
1076 char *cur;
1077 elfedit_optarg_item_t item;
1078
1079 /*
1080 * Estimate a worst case size for the usage string:
1081 * - module name
1082 * - lengths of the strings
1083 * - every option or argument is enclosed in brackets
1084 * - space in between each item, with an alternation (" | ")
1085 * - elipses will be displayed with each option and argument
1086 */
1087 n = strlen(mod->mod_name) + strlen(cmd->cmd_name[0]) + 6;
1088 elipses_len = strlen(MSG_ORIG(MSG_STR_ELIPSES));
1089 if ((optarg = cmd->cmd_opt) != NULL)
1090 while (optarg->oa_name != NULL) {
1091 elfedit_next_optarg(&optarg, &item);
1092 n += strlen(item.oai_name) + 5 + elipses_len;
1093 }
1094 if ((optarg = cmd->cmd_args) != NULL)
1095 while (optarg->oa_name != NULL) {
1096 elfedit_next_optarg(&optarg, &item);
1097 n += strlen(item.oai_name) + 5 + elipses_len;
1098 }
1099 n++; /* Null termination */
1100
1101 /*
1102 * If wrapping lines, we insert a newline and then wrap_str
1103 * every USAGE_WRAP_COL characters.
1104 */
1105 if (wrap_str != NULL)
1106 n += ((n + USAGE_WRAP_COL) / USAGE_WRAP_COL) *
1107 (strlen(wrap_str) + 1);
1108
1109 strbuf_ensure_size(&str, n);
1110
1111 /* Command name */
1112 cur = str.buf;
1113 n = str.n;
1114 if (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) == 0)
1115 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_SYSCMD),
1116 cmd->cmd_name[0]);
1117 else
1118 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_MODCMD),
1119 mod->mod_name, cmd->cmd_name[0]);
1120 cur += len;
1121 n -= len;
1122 cur_col += len;
1123
1124 if (cmd->cmd_opt != NULL)
1125 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_opt,
1126 1, wrap_str);
1127 if (cmd->cmd_args != NULL)
1128 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_args,
1129 0, wrap_str);
1130
1131 return (str.buf);
1132 }
1133
1134 /*
1135 * Wrapper on elfedit_msg() that issues an ELFEDIT_MSG_USAGE
1136 * error giving usage information for the command currently
1137 * referenced by state.cur_cmd.
1138 */
1139 void
elfedit_command_usage(void)1140 elfedit_command_usage(void)
1141 {
1142 elfedit_msg(ELFEDIT_MSG_CMDUSAGE, MSG_INTL(MSG_USAGE_CMD),
1143 elfedit_format_command_usage(state.cur_cmd->ucmd_mod,
1144 state.cur_cmd->ucmd_cmd, NULL, 0));
1145 }
1146
1147
1148 /*
1149 * This function allows the loadable modules to get the command line
1150 * flags.
1151 */
1152 elfedit_flag_t
elfedit_flags(void)1153 elfedit_flags(void)
1154 {
1155 return (state.flags);
1156 }
1157
1158 /*
1159 * This function is used to register a per-command invocation output style
1160 * that will momentarily override the global output style for the duration
1161 * of the current command. This function must only be called by an
1162 * active command.
1163 *
1164 * entry:
1165 * str - One of the valid strings for the output style
1166 */
1167 void
elfedit_set_cmd_outstyle(const char * str)1168 elfedit_set_cmd_outstyle(const char *str)
1169 {
1170 if ((state.cur_cmd != NULL) && (str != NULL)) {
1171 if (elfedit_atooutstyle(str, &state.cur_cmd->ucmd_ostyle) == 0)
1172 elfedit_msg(ELFEDIT_MSG_ERR,
1173 MSG_INTL(MSG_ERR_BADOSTYLE), str);
1174 state.cur_cmd->ucmd_ostyle_set = 1;
1175 }
1176 }
1177
1178 /*
1179 * This function allows the loadable modules to get the output style.
1180 */
1181 elfedit_outstyle_t
elfedit_outstyle(void)1182 elfedit_outstyle(void)
1183 {
1184 /*
1185 * If there is an active per-command output style,
1186 * return it.
1187 */
1188 if ((state.cur_cmd != NULL) && (state.cur_cmd->ucmd_ostyle_set))
1189 return (state.cur_cmd->ucmd_ostyle);
1190
1191
1192 return (state.outstyle);
1193 }
1194
1195 /*
1196 * Return the command descriptor of the currently executing command.
1197 * For use only by the modules or code called by the modules.
1198 */
1199 elfeditGC_cmd_t *
elfedit_curcmd(void)1200 elfedit_curcmd(void)
1201 {
1202 return (state.cur_cmd->ucmd_cmd);
1203 }
1204
1205 /*
1206 * Build a dynamically allocated elfedit_obj_state_t struct that
1207 * contains a cache of the ELF file contents. This pre-chewed form
1208 * is fed to each command, reducing the amount of ELF boilerplate
1209 * code each command needs to contain.
1210 *
1211 * entry:
1212 * file - Name of file to process
1213 *
1214 * exit:
1215 * Fills state.elf with the necessary information for the open file.
1216 *
1217 * note: The resulting elfedit_obj_state_t is allocated from a single
1218 * piece of memory, such that a single call to free() suffices
1219 * to release it as well as any memory it references.
1220 */
1221 static void
init_obj_state(const char * file)1222 init_obj_state(const char *file)
1223 {
1224 int fd;
1225 Elf *elf;
1226 int open_flag;
1227
1228 /*
1229 * In readonly mode, we open the file readonly so that it is
1230 * impossible to modify the file by accident. This also allows
1231 * us to access readonly files, perhaps in a case where we don't
1232 * intend to change it.
1233 *
1234 * We always use ELF_C_RDWR with elf_begin(), even in a readonly
1235 * session. This allows us to modify the in-memory image, which
1236 * can be useful when examining a file, even though we don't intend
1237 * to modify the on-disk data. The file is not writable in
1238 * this case, and we don't call elf_update(), so it is safe to do so.
1239 */
1240 open_flag = ((state.flags & ELFEDIT_F_READONLY) ? O_RDONLY : O_RDWR);
1241 if ((fd = open(file, open_flag)) == -1) {
1242 int err = errno;
1243 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNFILE),
1244 file, strerror(err));
1245 }
1246 (void) elf_version(EV_CURRENT);
1247 elf = elf_begin(fd, ELF_C_RDWR, NULL);
1248 if (elf == NULL) {
1249 (void) close(fd);
1250 elfedit_elferr(file, MSG_ORIG(MSG_ELF_BEGIN));
1251 /*NOTREACHED*/
1252 }
1253
1254 /* We only handle standalone ELF files */
1255 switch (elf_kind(elf)) {
1256 case ELF_K_AR:
1257 (void) close(fd);
1258 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOAR), file);
1259 break;
1260 case ELF_K_ELF:
1261 break;
1262 default:
1263 (void) close(fd);
1264 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNRECELFFILE),
1265 file);
1266 break;
1267 }
1268
1269 /*
1270 * Tell libelf that we take responsibility for object layout.
1271 * Otherwise, it will compute "proper" values for layout and
1272 * alignment fields, and these values can overwrite the values
1273 * set in the elfedit session. We are modifying existing
1274 * objects --- the layout concerns have already been dealt
1275 * with when the object was built.
1276 */
1277 (void) elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT);
1278
1279 /* Fill in state.elf.obj_state */
1280 state.elf.elfclass = gelf_getclass(elf);
1281 switch (state.elf.elfclass) {
1282 case ELFCLASS32:
1283 elfedit32_init_obj_state(file, fd, elf);
1284 break;
1285 case ELFCLASS64:
1286 elfedit64_init_obj_state(file, fd, elf);
1287 break;
1288 default:
1289 (void) close(fd);
1290 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADELFCLASS),
1291 file);
1292 break;
1293 }
1294 }
1295
1296
1297 #ifdef DEBUG_MODULE_LIST
1298 /*
1299 * Debug routine. Dump the module list to stdout.
1300 */
1301 static void
dbg_module_list(char * title)1302 dbg_module_list(char *title)
1303 {
1304 MODLIST_T *m;
1305
1306 printf("<MODULE LIST: %s>\n", title);
1307 for (m = state.modlist; m != NULL; m = m->next) {
1308 printf("Module: >%s<\n", m->mod->mod_name);
1309 printf(" hdl: %llx\n", m->dl_hdl);
1310 printf(" path: >%s<\n", m->path ? m->path : "<builtin>");
1311 }
1312 printf("<END OF MODULE LIST>\n");
1313 }
1314 #endif
1315
1316
1317 /*
1318 * Search the module list for the named module.
1319 *
1320 * entry:
1321 * name - Name of module to find
1322 * insdef - Address of variable to receive address of predecessor
1323 * node to the desired one.
1324 *
1325 * exit:
1326 * If the module is it is found, this routine returns the pointer to
1327 * its MODLIST_T structure. *insdef references the predecessor node, or
1328 * is NULL if the found item is at the head of the list.
1329 *
1330 * If the module is not found, NULL is returned. *insdef references
1331 * the predecessor node of the position where an entry for this module
1332 * would be placed, or NULL if it would go at the beginning.
1333 */
1334 static MODLIST_T *
module_loaded(const char * name,MODLIST_T ** insdef)1335 module_loaded(const char *name, MODLIST_T **insdef)
1336 {
1337 MODLIST_T *moddef;
1338 int cmp;
1339
1340 *insdef = NULL;
1341 moddef = state.modlist;
1342 if (moddef != NULL) {
1343 cmp = strcasecmp(name, moddef->ml_mod->mod_name);
1344 if (cmp == 0) { /* Desired module is first in list */
1345 return (moddef);
1346 } else if (cmp > 0) { /* cmp > 0: Insert in middle/end */
1347 *insdef = moddef;
1348 moddef = moddef->ml_next;
1349 cmp = -1;
1350 while (moddef && (cmp < 0)) {
1351 cmp = strcasecmp(moddef->ml_mod->mod_name,
1352 name);
1353 if (cmp == 0)
1354 return (moddef);
1355 if (cmp < 0) {
1356 *insdef = moddef;
1357 moddef = (*insdef)->ml_next;
1358 }
1359 }
1360 }
1361 }
1362
1363 return (NULL);
1364 }
1365
1366
1367 /*
1368 * Determine if a file is a sharable object based on its file path.
1369 * If path ends in a .so, followed optionally by a period and 1 or more
1370 * digits, we say that it is and return a pointer to the first character
1371 * of the suffix. Otherwise NULL is returned.
1372 */
1373 static const char *
path_is_so(const char * path)1374 path_is_so(const char *path)
1375 {
1376 int dotso_len;
1377 const char *tail;
1378 size_t len;
1379
1380 len = strlen(path);
1381 if (len == 0)
1382 return (NULL);
1383 tail = path + len;
1384 if (isdigit(*(tail - 1))) {
1385 while ((tail > path) && isdigit(*(tail - 1)))
1386 tail--;
1387 if ((tail <= path) || (*tail != '.'))
1388 return (NULL);
1389 }
1390 dotso_len = strlen(MSG_ORIG(MSG_STR_DOTSO));
1391 if ((tail - path) < dotso_len)
1392 return (NULL);
1393 tail -= dotso_len;
1394 if (strncmp(tail, MSG_ORIG(MSG_STR_DOTSO), dotso_len) == 0)
1395 return (tail);
1396
1397 return (NULL);
1398 }
1399
1400
1401 /*
1402 * Locate the start of the unsuffixed file name within path. Returns pointer
1403 * to first character of that name in path.
1404 *
1405 * entry:
1406 * path - Path to be examined.
1407 * tail - NULL, or pointer to position at tail of path from which
1408 * the search for '/' characters should start. If NULL,
1409 * strlen() is used to locate the end of the string.
1410 * buf - NULL, or buffer to receive a copy of the characters that
1411 * lie between the start of the filename and tail.
1412 * bufsize - sizeof(buf)
1413 *
1414 * exit:
1415 * The pointer to the first character of the unsuffixed file name
1416 * within path is returned. If buf is non-NULL, the characters
1417 * lying between that point and tail (or the end of path if tail
1418 * is NULL) are copied into buf.
1419 */
1420 static const char *
elfedit_basename(const char * path,const char * tail,char * buf,size_t bufsiz)1421 elfedit_basename(const char *path, const char *tail, char *buf, size_t bufsiz)
1422 {
1423 const char *s;
1424
1425 if (tail == NULL)
1426 tail = path + strlen(path);
1427 s = tail;
1428 while ((s > path) && (*(s - 1) != '/'))
1429 s--;
1430 if (buf != NULL)
1431 elfedit_strnbcpy(buf, s, tail - s, bufsiz);
1432 return (s);
1433 }
1434
1435
1436 /*
1437 * Issue an error on behalf of load_module(), taking care to release
1438 * resources that routine may have aquired:
1439 *
1440 * entry:
1441 * moddef - NULL, or a module definition to be released via free()
1442 * dl_hdl - NULL, or a handle to a sharable object to release via
1443 * dlclose().
1444 * dl_path - If dl_hdl is non-NULL, the path to the sharable object
1445 * file that was loaded.
1446 * format - A format string to pass to elfedit_msg(), containing
1447 * no more than (3) %s format codes, and no other format codes.
1448 * [s1-s4] - Strings to pass to elfedit_msg() to satisfy the four
1449 * allowed %s codes in format. Should be set to NULL if the
1450 * format string does not need them.
1451 *
1452 * note:
1453 * This routine makes a copy of the s1-s4 strings before freeing any
1454 * memory or unmapping the sharable library. It is therefore safe to
1455 * use strings from moddef, or from the sharable library (which will
1456 * be unmapped) to satisfy the other arguments s1-s4.
1457 */
1458 static void
load_module_err(MODLIST_T * moddef,void * dl_hdl,const char * dl_path,const char * format,const char * s1,const char * s2,const char * s3,const char * s4)1459 load_module_err(MODLIST_T *moddef, void *dl_hdl, const char *dl_path,
1460 const char *format, const char *s1, const char *s2, const char *s3,
1461 const char *s4)
1462 {
1463 #define SCRBUFSIZE (PATH_MAX + 256) /* A path, plus some extra */
1464
1465 char s1_buf[SCRBUFSIZE];
1466 char s2_buf[SCRBUFSIZE];
1467 char s3_buf[SCRBUFSIZE];
1468 char s4_buf[SCRBUFSIZE];
1469
1470 /*
1471 * The caller may provide strings for s1-s3 that are from
1472 * moddef. If we free moddef, the printf() will die on access
1473 * to free memory. We could push back on the user and force
1474 * each call to carefully make copies of such data. However, this
1475 * is an easy case to miss. Furthermore, this is an error case,
1476 * and machine efficiency is not the main issue. We therefore make
1477 * copies of the s1-s3 strings here into auto variables, and then
1478 * use those copies. The user is freed from worrying about it.
1479 *
1480 * We use oversized stack based buffers instead of malloc() to
1481 * reduce the number of ways that things can go wrong while
1482 * reporting the error.
1483 */
1484 if (s1 != NULL)
1485 (void) strlcpy(s1_buf, s1, sizeof (s1_buf));
1486 if (s2 != NULL)
1487 (void) strlcpy(s2_buf, s2, sizeof (s2_buf));
1488 if (s3 != NULL)
1489 (void) strlcpy(s3_buf, s3, sizeof (s3_buf));
1490 if (s4 != NULL)
1491 (void) strlcpy(s4_buf, s4, sizeof (s4_buf));
1492
1493
1494 if (moddef != NULL)
1495 free(moddef);
1496
1497 if ((dl_hdl != NULL) && (dlclose(dl_hdl) != 0))
1498 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
1499 dl_path, dlerror());
1500
1501 elfedit_msg(ELFEDIT_MSG_ERR, format, s1_buf, s2_buf, s3_buf, s4_buf);
1502 #undef SCRBUFSIZE
1503 }
1504
1505
1506 /*
1507 * Load a module sharable object for load_module().
1508 *
1509 * entry:
1510 * path - Path of file to open
1511 * moddef - If this function issues a non-returning error, it will
1512 * first return the memory referenced by moddef. This argument
1513 * is not used otherwise.
1514 * must_exist - If True, we consider it to be an error if the file given
1515 * by path does not exist. If False, no error is issued
1516 * and a NULL value is quietly returned.
1517 *
1518 * exit:
1519 * Returns a handle to the loaded object on success, or NULL if no
1520 * file was loaded.
1521 */
1522 static void *
load_module_dlopen(const char * path,MODLIST_T * moddef,int must_exist)1523 load_module_dlopen(const char *path, MODLIST_T *moddef, int must_exist)
1524 {
1525 int fd;
1526 void *hdl;
1527
1528 /*
1529 * If the file is not required to exist, and it doesn't, then
1530 * we want to quietly return without an error.
1531 */
1532 if (!must_exist) {
1533 fd = open(path, O_RDONLY);
1534 if (fd >= 0) {
1535 (void) close(fd);
1536 } else if (errno == ENOENT) {
1537 return (NULL);
1538 }
1539 }
1540
1541 if ((hdl = dlopen(path, RTLD_LAZY|RTLD_FIRST)) == NULL)
1542 load_module_err(moddef, NULL, NULL,
1543 MSG_INTL(MSG_ERR_CNTDLOPEN), path, dlerror(), NULL, NULL);
1544
1545 return (hdl);
1546 }
1547
1548
1549 /*
1550 * Sanity check option arguments to prevent common errors. The rest of
1551 * elfedit assumes these tests have been done, and does not check
1552 * again.
1553 */
1554 static void
validate_optarg(elfedit_cmd_optarg_t * optarg,int isopt,MODLIST_T * moddef,const char * mod_name,const char * cmd_name,void * dl_hdl,const char * dl_path)1555 validate_optarg(elfedit_cmd_optarg_t *optarg, int isopt, MODLIST_T *moddef,
1556 const char *mod_name, const char *cmd_name,
1557 void *dl_hdl, const char *dl_path)
1558 {
1559 #define FAIL(_msg) errmsg = _msg; goto fail
1560
1561 Msg errmsg;
1562 elfedit_cmd_oa_mask_t optmask = 0;
1563
1564 for (; optarg->oa_name != NULL; optarg++) {
1565 /*
1566 * If ELFEDIT_CMDOA_F_INHERIT is set:
1567 * - oa_name must be a value in the range of
1568 * known ELFEDIT_STDOA_ values.
1569 * - oa_help must be NULL
1570 * - ELFEDIT_CMDOA_F_INHERIT must be the only flag set
1571 */
1572 if (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) {
1573 if ((((uintptr_t)optarg->oa_name) >
1574 ELFEDIT_NUM_STDOA) ||
1575 (optarg->oa_help != 0) ||
1576 (optarg->oa_flags != ELFEDIT_CMDOA_F_INHERIT))
1577 /*
1578 * Can't use FAIL --- oa_name is not a valid
1579 * string, and load_module_err() looks at args.
1580 */
1581 load_module_err(moddef, dl_hdl, dl_path,
1582 MSG_INTL(MSG_ERR_BADSTDOA), dl_path,
1583 mod_name, cmd_name, NULL);
1584 continue;
1585 }
1586
1587 if (isopt) {
1588 /*
1589 * Option name must start with a '-', and must
1590 * have at one following character.
1591 */
1592 if (optarg->oa_name[0] != '-') {
1593 /* MSG_INTL(MSG_ERR_OPT_MODPRE) */
1594 FAIL(MSG_ERR_OPT_MODPRE);
1595 }
1596 if (optarg->oa_name[1] == '\0') {
1597 /* MSG_INTL(MSG_ERR_OPT_MODLEN) */
1598 FAIL(MSG_ERR_OPT_MODLEN);
1599 }
1600
1601 /*
1602 * oa_idmask must be 0, or it must have a single
1603 * bit set (a power of 2).oa_excmask must be 0
1604 * if oa_idmask is 0
1605 */
1606 if (optarg->oa_idmask == 0) {
1607 if (optarg->oa_excmask != 0) {
1608 /* MSG_INTL(MSG_ERR_OPT_EXCMASKN0) */
1609 FAIL(MSG_ERR_OPT_EXCMASKN0);
1610 }
1611 } else {
1612 if (elfedit_bits_set(optarg->oa_idmask,
1613 sizeof (optarg->oa_idmask)) != 1) {
1614 /* MSG_INTL(MSG_ERR_OPT_IDMASKPOW2) */
1615 FAIL(MSG_ERR_OPT_IDMASKPOW2);
1616 }
1617
1618 /* Non-zero idmask must be unique */
1619 if ((optarg->oa_idmask & optmask) != 0) {
1620 /* MSG_INTL(MSG_ERR_OPT_IDMASKUNIQ) */
1621 FAIL(MSG_ERR_OPT_IDMASKUNIQ);
1622 }
1623
1624 /* Add this one to the overall mask */
1625 optmask |= optarg->oa_idmask;
1626 }
1627 } else {
1628 /*
1629 * Argument name cannot start with a'-', and must
1630 * not be a null string.
1631 */
1632 if (optarg->oa_name[0] == '-') {
1633 /* MSG_INTL(MSG_ERR_ARG_MODPRE) */
1634 FAIL(MSG_ERR_ARG_MODPRE);
1635 }
1636 if (optarg->oa_name[1] == '\0') {
1637 /* MSG_INTL(MSG_ERR_ARG_MODLEN) */
1638 FAIL(MSG_ERR_ARG_MODLEN);
1639 }
1640
1641
1642 /* oa_idmask and oa_excmask must both be 0 */
1643 if ((optarg->oa_idmask != 0) ||
1644 (optarg->oa_excmask != 0)) {
1645 /* MSG_INTL(MSG_ERR_ARG_MASKNOT0) */
1646 FAIL(MSG_ERR_ARG_MASKNOT0);
1647 }
1648
1649 }
1650
1651 /*
1652 * If it takes a value, make sure that we are
1653 * processing options, because CMDOA_F_VALUE is not
1654 * allowed for plain arguments. Then check the following
1655 * item in the list:
1656 * - There must be a following item.
1657 * - oa_name must be non-NULL. This is the only field
1658 * that is used by elfedit.
1659 * - oa_help, oa_flags, oa_idmask, and oa_excmask
1660 * must be 0.
1661 */
1662 if (optarg->oa_flags & ELFEDIT_CMDOA_F_VALUE) {
1663 elfedit_cmd_optarg_t *oa1 = optarg + 1;
1664
1665 if (!isopt) {
1666 /* MSG_INTL(MSG_ERR_ARG_CMDOA_VAL) */
1667 FAIL(MSG_ERR_ARG_CMDOA_VAL);
1668 }
1669
1670 if ((optarg + 1)->oa_name == NULL) {
1671 /* MSG_INTL(MSG_ERR_BADMODOPTVAL) */
1672 FAIL(MSG_ERR_BADMODOPTVAL);
1673 }
1674
1675 if (oa1->oa_name == NULL) {
1676 /* MSG_INTL(MSG_ERR_CMDOA_VALNAM) */
1677 FAIL(MSG_ERR_CMDOA_VALNAM);
1678 }
1679 if ((oa1->oa_help != 0) || (oa1->oa_flags != 0) ||
1680 (oa1->oa_idmask != 0) || (oa1->oa_excmask != 0)) {
1681 /* MSG_INTL(MSG_ERR_CMDOA_VALNOT0) */
1682 FAIL(MSG_ERR_CMDOA_VALNOT0);
1683 }
1684 optarg++;
1685 }
1686 }
1687
1688
1689 return;
1690
1691 fail:
1692 load_module_err(moddef, dl_hdl, dl_path, MSG_INTL(errmsg),
1693 dl_path, mod_name, cmd_name, optarg->oa_name);
1694 }
1695
1696 /*
1697 * Look up the specified module, loading the module if necessary,
1698 * and return its definition, or NULL on failure.
1699 *
1700 * entry:
1701 * name - Name of module to load. If name contains a '/' character or has
1702 * a ".so" suffix, then it is taken to be an absolute file path,
1703 * and is used directly as is. If name does not contain a '/'
1704 * character, then we look for it against the locations in
1705 * the module path, addint the '.so' suffix, and taking the first
1706 * one we find.
1707 * must_exist - If True, we consider it to be an error if we are unable
1708 * to locate a file to load and the module does not already exist.
1709 * If False, NULL is returned quietly in this case.
1710 * allow_abs - True if absolute paths are allowed. False to disallow
1711 * them.
1712 *
1713 * note:
1714 * If the path is absolute, then we load the file and take the module
1715 * name from the data returned by its elfedit_init() function. If a
1716 * module of that name is already loaded, it is unloaded and replaced
1717 * with the new one.
1718 *
1719 * If the path is non absolute, then we check to see if the module has
1720 * already been loaded, and if so, we return that module definition.
1721 * In this case, nothing new is loaded. If the module has not been loaded,
1722 * we search the path for it and load it. If the module name provided
1723 * by the elfedit_init() function does not match the name of the file,
1724 * an error results.
1725 */
1726 elfeditGC_module_t *
elfedit_load_module(const char * name,int must_exist,int allow_abs)1727 elfedit_load_module(const char *name, int must_exist, int allow_abs)
1728 {
1729 elfedit_init_func_t *init_func;
1730 elfeditGC_module_t *mod;
1731 MODLIST_T *moddef, *insdef;
1732 const char *path;
1733 char path_buf[PATH_MAX + 1];
1734 void *hdl;
1735 size_t i;
1736 int is_abs_path;
1737 elfeditGC_cmd_t *cmd;
1738
1739 /*
1740 * If the name includes a .so suffix, or has any '/' characters,
1741 * then it is an absolute path that we use as is to load the named
1742 * file. Otherwise, we iterate over the path, adding the .so suffix
1743 * and load the first file that matches.
1744 */
1745 is_abs_path = (path_is_so(name) != NULL) ||
1746 (name != elfedit_basename(name, NULL, NULL, 0));
1747
1748 if (is_abs_path && !allow_abs)
1749 load_module_err(NULL, NULL, NULL,
1750 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
1751
1752 /*
1753 * If this is a non-absolute path, search for the module already
1754 * having been loaded, and return it if so.
1755 */
1756 if (!is_abs_path) {
1757 moddef = module_loaded(name, &insdef);
1758 if (moddef != NULL)
1759 return (moddef->ml_mod);
1760 /*
1761 * As a result of module_loaded(), insdef now contains the
1762 * immediate predecessor node for the new one, or NULL if
1763 * it goes at the front. In the absolute-path case, we take
1764 * care of this below, after the sharable object is loaded.
1765 */
1766 }
1767
1768 /*
1769 * malloc() a module definition block before trying to dlopen().
1770 * Doing things in the other order can cause the dlopen()'d object
1771 * to leak: If elfedit_malloc() fails, it can cause a jump to the
1772 * outer command loop without returning to the caller. Hence,
1773 * there will be no opportunity to clean up. Allocaing the module
1774 * first allows us to free it if necessary.
1775 */
1776 moddef = elfedit_malloc(MSG_INTL(MSG_ALLOC_MODDEF),
1777 sizeof (*moddef) + PATH_MAX + 1);
1778 moddef->ml_path = ((char *)moddef) + sizeof (*moddef);
1779
1780 if (is_abs_path) {
1781 path = name;
1782 hdl = load_module_dlopen(name, moddef, must_exist);
1783 } else {
1784 hdl = NULL;
1785 path = path_buf;
1786 for (i = 0; i < state.modpath.n; i++) {
1787 if (snprintf(path_buf, sizeof (path_buf),
1788 MSG_ORIG(MSG_FMT_BLDSOPATH), state.modpath.seg[i],
1789 name) > sizeof (path_buf))
1790 load_module_err(moddef, NULL, NULL,
1791 MSG_INTL(MSG_ERR_PATHTOOLONG),
1792 state.modpath.seg[i], name, NULL, NULL);
1793 hdl = load_module_dlopen(path, moddef, 0);
1794 }
1795 if (must_exist && (hdl == NULL))
1796 load_module_err(moddef, NULL, NULL,
1797 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL);
1798 }
1799
1800 if (hdl == NULL) {
1801 free(moddef);
1802 return (NULL);
1803 }
1804
1805 if (state.elf.elfclass == ELFCLASS32) {
1806 init_func = (elfedit_init_func_t *)
1807 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT32));
1808 } else {
1809 init_func = (elfedit_init_func_t *)
1810 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT64));
1811 }
1812 if (init_func == NULL)
1813 load_module_err(moddef, hdl, path,
1814 MSG_INTL(MSG_ERR_SONOTMOD), path, NULL, NULL, NULL);
1815
1816 /*
1817 * Note that the init function will be passing us an
1818 * elfedit[32|64]_module_t pointer, which we cast to the
1819 * generic module pointer type in order to be able to manage
1820 * either type with one set of code.
1821 */
1822 if (!(mod = (elfeditGC_module_t *)(* init_func)(ELFEDIT_VER_CURRENT)))
1823 load_module_err(moddef, hdl, path,
1824 MSG_INTL(MSG_ERR_BADMODLOAD), path, NULL, NULL, NULL);
1825
1826 /*
1827 * Enforce some rules, to help module developers:
1828 * - The primary name of a command must not be
1829 * the empty string ("").
1830 * - Options must start with a '-' followed by at least
1831 * one character.
1832 * - Arguments and options must be well formed.
1833 */
1834 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) {
1835 if (**cmd->cmd_name == '\0')
1836 load_module_err(moddef, hdl, path,
1837 MSG_INTL(MSG_ERR_NULLPRICMDNAM), mod->mod_name,
1838 NULL, NULL, NULL);
1839
1840 if (cmd->cmd_args != NULL)
1841 validate_optarg(cmd->cmd_args, 0, moddef, mod->mod_name,
1842 cmd->cmd_name[0], hdl, path);
1843 if (cmd->cmd_opt != NULL)
1844 validate_optarg(cmd->cmd_opt, 1, moddef, mod->mod_name,
1845 cmd->cmd_name[0], hdl, path);
1846 }
1847
1848 /*
1849 * Check the name the module provides. How we handle this depends
1850 * on whether the path is absolute or the result of a path search.
1851 */
1852 if (is_abs_path) {
1853 MODLIST_T *old_moddef = module_loaded(mod->mod_name, &insdef);
1854
1855 if (old_moddef != NULL) { /* Replace existing */
1856 free(moddef); /* Rare case: Don't need it */
1857 /*
1858 * Be sure we don't unload builtin modules!
1859 * These have a NULL dl_hdl field.
1860 */
1861 if (old_moddef->ml_dl_hdl == NULL)
1862 load_module_err(NULL, hdl, path,
1863 MSG_INTL(MSG_ERR_CNTULSMOD),
1864 old_moddef->ml_mod->mod_name, NULL,
1865 NULL, NULL);
1866
1867 /* Unload existing */
1868 if (dlclose(old_moddef->ml_dl_hdl) != 0)
1869 elfedit_msg(ELFEDIT_MSG_ERR,
1870 MSG_INTL(MSG_ERR_CNTDLCLOSE),
1871 old_moddef->ml_path, dlerror());
1872 elfedit_msg(ELFEDIT_MSG_DEBUG,
1873 MSG_INTL(MSG_DEBUG_MODUNLOAD),
1874 old_moddef->ml_mod->mod_name, old_moddef->ml_path);
1875 old_moddef->ml_mod = mod;
1876 old_moddef->ml_dl_hdl = hdl;
1877 (void) strlcpy((char *)old_moddef->ml_path, path,
1878 PATH_MAX + 1);
1879 elfedit_msg(ELFEDIT_MSG_DEBUG,
1880 MSG_INTL(MSG_DEBUG_MODLOAD),
1881 old_moddef->ml_mod->mod_name, path);
1882 return (old_moddef->ml_mod);
1883 }
1884 /*
1885 * insdef now contains the insertion point for the absolute
1886 * path case.
1887 */
1888 } else {
1889 /* If the names don't match, then error */
1890 if (strcasecmp(name, mod->mod_name) != 0)
1891 load_module_err(moddef, hdl, path,
1892 MSG_INTL(MSG_ERR_BADMODNAME),
1893 mod->mod_name, name, path, NULL);
1894 }
1895
1896 /*
1897 * Link module into the module list. If insdef is NULL,
1898 * it goes at the head. If insdef is non-NULL, it goes immediately
1899 * after
1900 */
1901 if (insdef == NULL) {
1902 moddef->ml_next = state.modlist;
1903 state.modlist = moddef;
1904 } else {
1905 moddef->ml_next = insdef->ml_next;
1906 insdef->ml_next = moddef;
1907 }
1908 moddef->ml_mod = mod;
1909 moddef->ml_dl_hdl = hdl;
1910 (void) strlcpy((char *)moddef->ml_path, path, PATH_MAX + 1);
1911
1912 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODLOAD),
1913 moddef->ml_mod->mod_name, path);
1914
1915 return (moddef->ml_mod);
1916 }
1917
1918
1919 /*
1920 * Unload the specified module
1921 */
1922 void
elfedit_unload_module(const char * name)1923 elfedit_unload_module(const char *name)
1924 {
1925 MODLIST_T *moddef, *insdef;
1926
1927 moddef = module_loaded(name, &insdef);
1928 if (moddef == NULL)
1929 return;
1930
1931 /* Built in modules cannot be unloaded. They have a NULL dl_hdl field */
1932 if (moddef->ml_dl_hdl == NULL)
1933 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTULSMOD),
1934 moddef->ml_mod->mod_name);
1935
1936 /*
1937 * When we unload it, the name string goes with it. So
1938 * announce it while we still can without having to make a copy.
1939 */
1940 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODUNLOAD),
1941 moddef->ml_mod->mod_name, moddef->ml_path);
1942
1943 /*
1944 * Close it before going further. On failure, we'll jump, and the
1945 * record will remain in the module list. On success,
1946 * we'll retain control, and can safely remove it.
1947 */
1948 if (dlclose(moddef->ml_dl_hdl) != 0)
1949 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE),
1950 moddef->ml_path, dlerror());
1951
1952 /* Unlink the record from the module list */
1953 if (insdef == NULL)
1954 state.modlist = moddef->ml_next;
1955 else
1956 insdef->ml_next = moddef->ml_next;
1957
1958 /* Release the memory */
1959 free(moddef);
1960 }
1961
1962
1963 /*
1964 * Load all sharable objects found in the specified directory.
1965 *
1966 * entry:
1967 * dirpath - Path of directory to process.
1968 * must_exist - If True, it is an error if diropen() fails to open
1969 * the given directory. Of False, we quietly ignore it and return.
1970 * abs_path - If True, files are loaded using their literal paths.
1971 * If False, their module name is extracted from the dirpath
1972 * and a path based search is used to locate it.
1973 */
1974 void
elfedit_load_moddir(const char * dirpath,int must_exist,int abs_path)1975 elfedit_load_moddir(const char *dirpath, int must_exist, int abs_path)
1976 {
1977 char path[PATH_MAX + 1];
1978 DIR *dir;
1979 struct dirent *dp;
1980 const char *tail;
1981
1982 dir = opendir(dirpath);
1983 if (dir == NULL) {
1984 int err = errno;
1985
1986 if (!must_exist && (err == ENOENT))
1987 return;
1988 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNDIR),
1989 dirpath, strerror(err));
1990 /*NOTREACHED*/
1991 }
1992
1993 while ((dp = readdir(dir)) != NULL) {
1994 if ((tail = path_is_so(dp->d_name)) != NULL) {
1995 if (abs_path) {
1996 (void) snprintf(path, sizeof (path),
1997 MSG_ORIG(MSG_FMT_BLDPATH), dirpath,
1998 dp->d_name);
1999 } else {
2000 (void) elfedit_basename(dp->d_name, tail,
2001 path, sizeof (path));
2002 }
2003 (void) elfedit_load_module(path, must_exist, 1);
2004 }
2005 }
2006 (void) closedir(dir);
2007 }
2008
2009
2010 /*
2011 * Follow the module load path, and load the first module found for each
2012 * given name.
2013 */
2014 void
elfedit_load_modpath(void)2015 elfedit_load_modpath(void)
2016 {
2017 size_t i;
2018
2019 for (i = 0; i < state.modpath.n; i++)
2020 elfedit_load_moddir(state.modpath.seg[i], 0, 0);
2021 }
2022
2023 /*
2024 * Given a module definition, look for the specified command.
2025 * Returns the command if found, and NULL otherwise.
2026 */
2027 static elfeditGC_cmd_t *
find_cmd(elfeditGC_module_t * mod,const char * name)2028 find_cmd(elfeditGC_module_t *mod, const char *name)
2029 {
2030 elfeditGC_cmd_t *cmd;
2031 const char **cmd_name;
2032
2033 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
2034 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
2035 if (strcasecmp(name, *cmd_name) == 0) {
2036 if (cmd_name != cmd->cmd_name)
2037 elfedit_msg(ELFEDIT_MSG_DEBUG,
2038 MSG_INTL(MSG_DEBUG_CMDALIAS),
2039 mod->mod_name, *cmd_name,
2040 mod->mod_name, *cmd->cmd_name);
2041 return (cmd);
2042 }
2043
2044 return (NULL);
2045 }
2046
2047
2048 /*
2049 * Given a command name, return its command definition.
2050 *
2051 * entry:
2052 * name - Command to be looked up
2053 * must_exist - If True, we consider it to be an error if the command
2054 * does not exist. If False, NULL is returned quietly in
2055 * this case.
2056 * mod_ret - NULL, or address of a variable to receive the
2057 * module definition block of the module containing
2058 * the command.
2059 *
2060 * exit:
2061 * On success, returns a pointer to the command definition, and
2062 * if mod_ret is non-NULL, *mod_ret receives a pointer to the
2063 * module definition. On failure, must_exist determines the
2064 * action taken: If must_exist is True, an error is issued and
2065 * control does not return to the caller. If must_exist is False,
2066 * NULL is quietly returned.
2067 *
2068 * note:
2069 * A ':' in name is used to delimit the module and command names.
2070 * If it is omitted, or if it is the first non-whitespace character
2071 * in the name, then the built in sys: module is implied.
2072 */
2073 elfeditGC_cmd_t *
elfedit_find_command(const char * name,int must_exist,elfeditGC_module_t ** mod_ret)2074 elfedit_find_command(const char *name, int must_exist,
2075 elfeditGC_module_t **mod_ret)
2076 {
2077 elfeditGC_module_t *mod;
2078 const char *mod_str;
2079 const char *cmd_str;
2080 char mod_buf[ELFEDIT_MAXMODNAM + 1];
2081 size_t n;
2082 elfeditGC_cmd_t *cmd;
2083
2084
2085 cmd_str = strstr(name, MSG_ORIG(MSG_STR_COLON));
2086 if (cmd_str == NULL) { /* No module name -> sys: */
2087 mod_str = MSG_ORIG(MSG_MOD_SYS);
2088 cmd_str = name;
2089 } else if (cmd_str == name) { /* Empty module name -> sys: */
2090 mod_str = MSG_ORIG(MSG_MOD_SYS);
2091 cmd_str++; /* Skip the colon */
2092 } else { /* Have both module and command */
2093 n = cmd_str - name;
2094 if (n >= sizeof (mod_buf)) {
2095 if (must_exist)
2096 elfedit_msg(ELFEDIT_MSG_ERR,
2097 MSG_INTL(MSG_ERR_MODNAMTOOLONG), name);
2098 return (NULL);
2099 }
2100 (void) strlcpy(mod_buf, name, n + 1);
2101 mod_str = mod_buf;
2102 cmd_str++;
2103 }
2104
2105 /* Lookup/load module. Won't return on error */
2106 mod = elfedit_load_module(mod_str, must_exist, 0);
2107 if (mod == NULL)
2108 return (NULL);
2109
2110 /* Locate the command */
2111 cmd = find_cmd(mod, cmd_str);
2112 if (cmd == NULL) {
2113 if (must_exist) {
2114 /*
2115 * Catch empty command in order to provide
2116 * a better error message.
2117 */
2118 if (*cmd_str == '\0') {
2119 elfedit_msg(ELFEDIT_MSG_ERR,
2120 MSG_INTL(MSG_ERR_MODNOCMD), mod_str);
2121 } else {
2122 elfedit_msg(ELFEDIT_MSG_ERR,
2123 MSG_INTL(MSG_ERR_UNRECCMD),
2124 mod_str, cmd_str);
2125 }
2126 }
2127 } else {
2128 if (mod_ret != NULL)
2129 *mod_ret = mod;
2130 }
2131 return (cmd);
2132 }
2133
2134
2135 /*
2136 * Release all user command blocks found on state.ucmd
2137 */
2138 static void
free_user_cmds(void)2139 free_user_cmds(void)
2140 {
2141 USER_CMD_T *next;
2142
2143 while (state.ucmd.list) {
2144 next = state.ucmd.list->ucmd_next;
2145 free(state.ucmd.list);
2146 state.ucmd.list = next;
2147 }
2148 state.ucmd.tail = NULL;
2149 state.ucmd.n = 0;
2150 state.cur_cmd = NULL;
2151 }
2152
2153
2154 /*
2155 * Process all user command blocks found on state.ucmd, and then
2156 * remove them from the list.
2157 */
2158 static void
dispatch_user_cmds()2159 dispatch_user_cmds()
2160 {
2161 USER_CMD_T *ucmd;
2162 elfedit_cmdret_t cmd_ret;
2163
2164 ucmd = state.ucmd.list;
2165 if (ucmd) {
2166 /* Do them, in order */
2167 for (; ucmd; ucmd = ucmd->ucmd_next) {
2168 state.cur_cmd = ucmd;
2169 if (!state.msg_jbuf.active)
2170 elfedit_msg(ELFEDIT_MSG_DEBUG,
2171 MSG_INTL(MSG_DEBUG_EXECCMD),
2172 ucmd->ucmd_orig_str);
2173 /*
2174 * The cmd_func field is the generic definition.
2175 * We need to cast it to the type that matches
2176 * the proper ELFCLASS before calling it.
2177 */
2178 if (state.elf.elfclass == ELFCLASS32) {
2179 elfedit32_cmd_func_t *cmd_func =
2180 (elfedit32_cmd_func_t *)
2181 ucmd->ucmd_cmd->cmd_func;
2182
2183 cmd_ret = (* cmd_func)(state.elf.obj_state.s32,
2184 ucmd->ucmd_argc, ucmd->ucmd_argv);
2185 } else {
2186 elfedit64_cmd_func_t *cmd_func =
2187 (elfedit64_cmd_func_t *)
2188 ucmd->ucmd_cmd->cmd_func;
2189
2190 cmd_ret = (* cmd_func)(state.elf.obj_state.s64,
2191 ucmd->ucmd_argc, ucmd->ucmd_argv);
2192 }
2193 state.cur_cmd = NULL;
2194 /* If a pager was started, wrap it up */
2195 elfedit_pager_cleanup();
2196
2197 switch (cmd_ret) {
2198 case ELFEDIT_CMDRET_MOD_OS_MACH:
2199 /*
2200 * Inform the elfconst module that the machine
2201 * or osabi has has changed. It may be necessary
2202 * to fetch new strings from libconv.
2203 */
2204 state.elf.elfconst_ehdr_change = 1;
2205 /*FALLTHROUGH*/
2206 case ELFEDIT_CMDRET_MOD:
2207 /*
2208 * Command modified the output ELF image,
2209 * mark the file as needing a flush to disk.
2210 */
2211 state.file.dirty = 1;
2212 break;
2213 case ELFEDIT_CMDRET_FLUSH:
2214 /*
2215 * Command flushed the output file,
2216 * clear the dirty bit.
2217 */
2218 state.file.dirty = 0;
2219 break;
2220 case ELFEDIT_CMDRET_NONE:
2221 break;
2222 }
2223 }
2224 free_user_cmds();
2225 }
2226 }
2227
2228
2229 /*
2230 * Prepare a GETTOK_STATE struct for gettok().
2231 *
2232 * entry:
2233 * gettok_state - gettok state block to use
2234 * str - Writable buffer to tokenize. Note that gettok()
2235 * is allowed to change the contents of this buffer.
2236 * inc_null_final - If the line ends in whitespace instead of
2237 * immediately hitting a NULL, and inc_null_final is TRUE,
2238 * then a null final token is generated. Otherwise trailing
2239 * whitespace is ignored.
2240 */
2241 static void
gettok_init(GETTOK_STATE * gettok_state,char * buf,int inc_null_final)2242 gettok_init(GETTOK_STATE *gettok_state, char *buf, int inc_null_final)
2243 {
2244 gettok_state->gtok_buf = gettok_state->gtok_cur_buf = buf;
2245 gettok_state->gtok_inc_null_final = inc_null_final;
2246 gettok_state->gtok_null_seen = 0;
2247 }
2248
2249
2250 /*
2251 * Locate the next token from the buffer.
2252 *
2253 * entry:
2254 * gettok_state - State of gettok() operation. Initialized
2255 * by gettok_init(), and passed to gettok().
2256 *
2257 * exit:
2258 * If a token is found, gettok_state->gtok_last_token is filled in
2259 * with the details and True (1) is returned. If no token is found,
2260 * False (1) is returned, and the contents of
2261 * gettok_state->gtok_last_token are undefined.
2262 *
2263 * note:
2264 * - The token returned references the memory in gettok_state->gtok_buf.
2265 * The caller should not modify the buffer until all such
2266 * pointers have been discarded.
2267 * - This routine will modify the contents of gettok_state->gtok_buf
2268 * as necessary to remove quotes and eliminate escape
2269 * (\)characters.
2270 */
2271 static int
gettok(GETTOK_STATE * gettok_state)2272 gettok(GETTOK_STATE *gettok_state)
2273 {
2274 char *str = gettok_state->gtok_cur_buf;
2275 char *look;
2276 int quote_ch = '\0';
2277
2278 /* Skip leading whitespace */
2279 while (isspace(*str))
2280 str++;
2281
2282 if (*str == '\0') {
2283 /*
2284 * If user requested it, and there was whitespace at the
2285 * end, then generate one last null token.
2286 */
2287 if (gettok_state->gtok_inc_null_final &&
2288 !gettok_state->gtok_null_seen) {
2289 gettok_state->gtok_inc_null_final = 0;
2290 gettok_state->gtok_null_seen = 1;
2291 gettok_state->gtok_last_token.tok_str = str;
2292 gettok_state->gtok_last_token.tok_len = 0;
2293 gettok_state->gtok_last_token.tok_line_off =
2294 str - gettok_state->gtok_buf;
2295 return (1);
2296 }
2297 gettok_state->gtok_null_seen = 1;
2298 return (0);
2299 }
2300
2301 /*
2302 * Read token: The standard delimiter is whitespace, but
2303 * we honor either single or double quotes. Also, we honor
2304 * backslash escapes.
2305 */
2306 gettok_state->gtok_last_token.tok_str = look = str;
2307 gettok_state->gtok_last_token.tok_line_off =
2308 look - gettok_state->gtok_buf;
2309 for (; *look; look++) {
2310 if (*look == quote_ch) { /* Terminates active quote */
2311 quote_ch = '\0';
2312 continue;
2313 }
2314
2315 if (quote_ch == '\0') { /* No quote currently active */
2316 if ((*look == '\'') || (*look == '"')) {
2317 quote_ch = *look; /* New active quote */
2318 continue;
2319 }
2320 if (isspace(*look))
2321 break;
2322 }
2323
2324 /*
2325 * The semantics of the backslash character depends on
2326 * the quote style in use:
2327 * - Within single quotes, backslash is not
2328 * an escape character, and is taken literally.
2329 * - If outside of quotes, the backslash is an escape
2330 * character. The backslash is ignored and the
2331 * following character is taken literally, losing
2332 * any special properties it normally has.
2333 * - Within double quotes, backslash works like a
2334 * backslash escape within a C literal. Certain
2335 * escapes are recognized and replaced with their
2336 * special character. Any others are an error.
2337 */
2338 if (*look == '\\') {
2339 if (quote_ch == '\'') {
2340 *str++ = *look;
2341 continue;
2342 }
2343
2344 look++;
2345 if (*look == '\0') { /* Esc applied to NULL term? */
2346 elfedit_msg(ELFEDIT_MSG_ERR,
2347 MSG_INTL(MSG_ERR_ESCEOL));
2348 /*NOTREACHED*/
2349 }
2350
2351 if (quote_ch == '"') {
2352 int ch = conv_translate_c_esc(&look);
2353
2354 if (ch == -1)
2355 elfedit_msg(ELFEDIT_MSG_ERR,
2356 MSG_INTL(MSG_ERR_BADCESC), *look);
2357 *str++ = ch;
2358 look--; /* for() will advance by 1 */
2359 continue;
2360 }
2361 }
2362
2363 if (look != str)
2364 *str = *look;
2365 str++;
2366 }
2367
2368 /* Don't allow unterminated quoted tokens */
2369 if (quote_ch != '\0')
2370 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNTERMQUOTE),
2371 quote_ch);
2372
2373 gettok_state->gtok_last_token.tok_len = str -
2374 gettok_state->gtok_last_token.tok_str;
2375 gettok_state->gtok_null_seen = *look == '\0';
2376 if (!gettok_state->gtok_null_seen)
2377 look++;
2378 *str = '\0';
2379 gettok_state->gtok_cur_buf = look;
2380
2381 #ifdef DEBUG_GETTOK
2382 printf("GETTOK >");
2383 elfedit_str_to_c_literal(gettok_state->gtok_last_token.tok_str,
2384 elfedit_write);
2385 printf("< \tlen(%d) offset(%d)\n",
2386 gettok_state->gtok_last_token.tok_len,
2387 gettok_state->gtok_last_token.tok_line_off);
2388 #endif
2389
2390 return (1);
2391 }
2392
2393
2394 /*
2395 * Tokenize the user command string, and return a pointer to the
2396 * TOK_STATE buffer maintained by this function. That buffer contains
2397 * the tokenized strings.
2398 *
2399 * entry:
2400 * user_cmd_str - String to tokenize
2401 * len - # of characters in user_cmd_str to examine. If
2402 * (len < 0), then the complete string is processed
2403 * stopping with the NULL termination. Otherwise,
2404 * processing stops after len characters, and any
2405 * remaining characters are ignored.
2406 * inc_null_final - If True, and if user_cmd_str has whitespace
2407 * at the end following the last non-null token, then
2408 * a final null token will be included. If False, null
2409 * tokens are ignored.
2410 *
2411 * note:
2412 * This routine returns pointers to internally allocated memory.
2413 * The caller must not alter anything contained in the TOK_STATE
2414 * buffer returned. Furthermore, the the contents of TOK_STATE
2415 * are only valid until the next call to tokenize_user_cmd().
2416 */
2417 static TOK_STATE *
tokenize_user_cmd(const char * user_cmd_str,size_t len,int inc_null_final)2418 tokenize_user_cmd(const char *user_cmd_str, size_t len, int inc_null_final)
2419 {
2420 #define INITIAL_TOK_ALLOC 5
2421
2422 /*
2423 * As we parse the user command, we need temporary space to
2424 * hold the tokens. We do this by dynamically allocating a string
2425 * buffer and a token array, and doubling them as necessary. This
2426 * is a single threaded application, so static variables suffice.
2427 */
2428 static STRBUF str;
2429 static TOK_STATE tokst;
2430
2431 GETTOK_STATE gettok_state;
2432 size_t n;
2433
2434 /*
2435 * Make a copy we can modify. If (len == 0), take the entire
2436 * string. Otherwise limit it to the specified length.
2437 */
2438 tokst.tokst_cmd_len = strlen(user_cmd_str);
2439 if ((len > 0) && (len < tokst.tokst_cmd_len))
2440 tokst.tokst_cmd_len = len;
2441 tokst.tokst_cmd_len++; /* Room for NULL termination */
2442 strbuf_ensure_size(&str, tokst.tokst_cmd_len);
2443 (void) strlcpy(str.buf, user_cmd_str, tokst.tokst_cmd_len);
2444
2445 /* Trim off any newline character that might be present */
2446 if ((tokst.tokst_cmd_len > 1) &&
2447 (str.buf[tokst.tokst_cmd_len - 2] == '\n')) {
2448 tokst.tokst_cmd_len--;
2449 str.buf[tokst.tokst_cmd_len - 1] = '\0';
2450 }
2451
2452 /* Tokenize the user command string into tok struct */
2453 gettok_init(&gettok_state, str.buf, inc_null_final);
2454 tokst.tokst_str_size = 0; /* Space needed for token strings */
2455 for (tokst.tokst_cnt = 0; gettok(&gettok_state) != 0;
2456 tokst.tokst_cnt++) {
2457 /* If we need more room, expand the token buffer */
2458 if (tokst.tokst_cnt >= tokst.tokst_bufsize) {
2459 n = (tokst.tokst_bufsize == 0) ?
2460 INITIAL_TOK_ALLOC : (tokst.tokst_bufsize * 2);
2461 tokst.tokst_buf = elfedit_realloc(
2462 MSG_INTL(MSG_ALLOC_TOKBUF), tokst.tokst_buf,
2463 n * sizeof (*tokst.tokst_buf));
2464 tokst.tokst_bufsize = n;
2465 }
2466 tokst.tokst_str_size +=
2467 gettok_state.gtok_last_token.tok_len + 1;
2468 tokst.tokst_buf[tokst.tokst_cnt] = gettok_state.gtok_last_token;
2469 }
2470 /* fold the command token to lowercase */
2471 if (tokst.tokst_cnt > 0) {
2472 char *s;
2473
2474 for (s = tokst.tokst_buf[0].tok_str; *s; s++)
2475 if (isupper(*s))
2476 *s = tolower(*s);
2477 }
2478
2479 return (&tokst);
2480
2481 #undef INITIAL_TOK_ALLOC
2482 }
2483
2484
2485 /*
2486 * Parse the user command string, and put an entry for it at the end
2487 * of state.ucmd.
2488 */
2489 static void
parse_user_cmd(const char * user_cmd_str)2490 parse_user_cmd(const char *user_cmd_str)
2491 {
2492 TOK_STATE *tokst;
2493 char *s;
2494 size_t n;
2495 size_t len;
2496 USER_CMD_T *ucmd;
2497 elfeditGC_module_t *mod;
2498 elfeditGC_cmd_t *cmd;
2499
2500 /*
2501 * Break it into tokens. If there are none, then it is
2502 * an empty command and is ignored.
2503 */
2504 tokst = tokenize_user_cmd(user_cmd_str, -1, 0);
2505 if (tokst->tokst_cnt == 0)
2506 return;
2507
2508 /* Find the command. Won't return on error */
2509 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 1, &mod);
2510
2511 /*
2512 * If there is no ELF file being edited, then only commands
2513 * from the sys: module are allowed.
2514 */
2515 if ((state.file.present == 0) &&
2516 (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) != 0))
2517 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFILSYSONLY),
2518 mod->mod_name, cmd->cmd_name[0]);
2519
2520
2521 /* Allocate, fill in, and insert a USER_CMD_T block */
2522 n = S_DROUND(sizeof (USER_CMD_T));
2523 ucmd = elfedit_malloc(MSG_INTL(MSG_ALLOC_UCMD),
2524 n + (sizeof (char *) * (tokst->tokst_cnt - 1)) +
2525 tokst->tokst_cmd_len + tokst->tokst_str_size);
2526 ucmd->ucmd_next = NULL;
2527 ucmd->ucmd_argc = tokst->tokst_cnt - 1;
2528 /*LINTED E_BAD_PTR_CAST_ALIGN*/
2529 ucmd->ucmd_argv = (const char **)(n + (char *)ucmd);
2530 ucmd->ucmd_orig_str = (char *)(ucmd->ucmd_argv + ucmd->ucmd_argc);
2531 (void) strncpy(ucmd->ucmd_orig_str, user_cmd_str, tokst->tokst_cmd_len);
2532 ucmd->ucmd_mod = mod;
2533 ucmd->ucmd_cmd = cmd;
2534 ucmd->ucmd_ostyle_set = 0;
2535 s = ucmd->ucmd_orig_str + tokst->tokst_cmd_len;
2536 for (n = 1; n < tokst->tokst_cnt; n++) {
2537 len = tokst->tokst_buf[n].tok_len + 1;
2538 ucmd->ucmd_argv[n - 1] = s;
2539 (void) strncpy(s, tokst->tokst_buf[n].tok_str, len);
2540 s += len;
2541 }
2542 if (state.ucmd.list == NULL) {
2543 state.ucmd.list = state.ucmd.tail = ucmd;
2544 } else {
2545 state.ucmd.tail->ucmd_next = ucmd;
2546 state.ucmd.tail = ucmd;
2547 }
2548 state.ucmd.n++;
2549 }
2550
2551
2552 /*
2553 * Copy infile to a new file with the name given by outfile.
2554 */
2555 static void
create_outfile(const char * infile,const char * outfile)2556 create_outfile(const char *infile, const char *outfile)
2557 {
2558 pid_t pid;
2559 int statloc;
2560 struct stat statbuf;
2561
2562
2563 pid = fork();
2564 switch (pid) {
2565 case -1: /* Unable to create process */
2566 {
2567 int err = errno;
2568 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTFORK),
2569 strerror(err));
2570 }
2571 /*NOTREACHED*/
2572 return;
2573
2574 case 0:
2575 (void) execl(MSG_ORIG(MSG_STR_BINCP),
2576 MSG_ORIG(MSG_STR_BINCP), infile, outfile, NULL);
2577 /*
2578 * exec() only returns on error. This is the child process,
2579 * so we want to stay away from the usual error mechanism
2580 * and handle things directly.
2581 */
2582 {
2583 int err = errno;
2584 (void) fprintf(stderr, MSG_INTL(MSG_ERR_CNTEXEC),
2585 MSG_ORIG(MSG_STR_ELFEDIT),
2586 MSG_ORIG(MSG_STR_BINCP), strerror(err));
2587 }
2588 exit(1);
2589 /*NOTREACHED*/
2590 }
2591
2592 /* This is the parent: Wait for the child to terminate */
2593 if (waitpid(pid, &statloc, 0) != pid) {
2594 int err = errno;
2595 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTWAIT),
2596 strerror(err));
2597 }
2598 /*
2599 * If the child failed, then terminate the process. There is no
2600 * need for an error message, because the child will have taken
2601 * care of that.
2602 */
2603 if (!WIFEXITED(statloc) || (WEXITSTATUS(statloc) != 0))
2604 exit(1);
2605
2606 /* Make sure the copy allows user write access */
2607 if (stat(outfile, &statbuf) == -1) {
2608 int err = errno;
2609 (void) unlink(outfile);
2610 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTSTAT),
2611 outfile, strerror(err));
2612 }
2613 if ((statbuf.st_mode & S_IWUSR) == 0) {
2614 /* Only keep permission bits, and add user write */
2615 statbuf.st_mode |= S_IWUSR;
2616 statbuf.st_mode &= 07777; /* Only keep the permission bits */
2617 if (chmod(outfile, statbuf.st_mode) == -1) {
2618 int err = errno;
2619 (void) unlink(outfile);
2620 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTCHMOD),
2621 outfile, strerror(err));
2622 }
2623 }
2624 }
2625
2626 /*
2627 * Given a module path string, determine how long the resulting path will
2628 * be when all % tokens have been expanded.
2629 *
2630 * entry:
2631 * path - Path for which expanded length is desired
2632 * origin_root - Root of $ORIGIN tree containing running elfedit program
2633 *
2634 * exit:
2635 * Returns the value strlen() will give for the expanded path.
2636 */
2637 static size_t
modpath_strlen(const char * path,const char * origin_root)2638 modpath_strlen(const char *path, const char *origin_root)
2639 {
2640 size_t len = 0;
2641 const char *s;
2642
2643 s = path;
2644 len = 0;
2645 for (s = path; *s != '\0'; s++) {
2646 if (*s == '%') {
2647 s++;
2648 switch (*s) {
2649 case 'i': /* ISA of running elfedit */
2650 len += strlen(isa_i_str);
2651 break;
2652 case 'I': /* "" for 32-bit, same as %i for 64 */
2653 len += strlen(isa_I_str);
2654 break;
2655 case 'o': /* Insert default path */
2656 len +=
2657 modpath_strlen(MSG_ORIG(MSG_STR_MODPATH),
2658 origin_root);
2659 break;
2660 case 'r': /* root of tree with running elfedit */
2661 len += strlen(origin_root);
2662 break;
2663
2664 case '%': /* %% is reduced to just '%' */
2665 len++;
2666 break;
2667 default: /* All other % codes are reserved */
2668 elfedit_msg(ELFEDIT_MSG_ERR,
2669 MSG_INTL(MSG_ERR_BADPATHCODE), *s);
2670 /*NOTREACHED*/
2671 break;
2672 }
2673 } else { /* Non-% character passes straight through */
2674 len++;
2675 }
2676 }
2677
2678 return (len);
2679 }
2680
2681
2682 /*
2683 * Given a module path string, and a buffer large enough to hold the results,
2684 * fill the buffer with the expanded path.
2685 *
2686 * entry:
2687 * path - Path for which expanded length is desired
2688 * origin_root - Root of tree containing running elfedit program
2689 * buf - Buffer to receive the result. buf must as large or larger
2690 * than the value given by modpath_strlen().
2691 *
2692 * exit:
2693 * Returns pointer to location following the last character
2694 * written to buf. A NULL byte is written to that address.
2695 */
2696 static char *
modpath_expand(const char * path,const char * origin_root,char * buf)2697 modpath_expand(const char *path, const char *origin_root, char *buf)
2698 {
2699 size_t len;
2700 const char *cp_str;
2701
2702 for (; *path != '\0'; path++) {
2703 if (*path == '%') {
2704 path++;
2705 cp_str = NULL;
2706 switch (*path) {
2707 case 'i': /* ISA of running elfedit */
2708 cp_str = isa_i_str;
2709 break;
2710 case 'I': /* "" for 32-bit, same as %i for 64 */
2711 cp_str = isa_I_str;
2712 break;
2713 case 'o': /* Insert default path */
2714 buf = modpath_expand(MSG_ORIG(MSG_STR_MODPATH),
2715 origin_root, buf);
2716 break;
2717 case 'r':
2718 cp_str = origin_root;
2719 break;
2720 case '%': /* %% is reduced to just '%' */
2721 *buf++ = *path;
2722 break;
2723 default: /* All other % codes are reserved */
2724 elfedit_msg(ELFEDIT_MSG_ERR,
2725 MSG_INTL(MSG_ERR_BADPATHCODE), *path);
2726 /*NOTREACHED*/
2727 break;
2728 }
2729 if ((cp_str != NULL) && ((len = strlen(cp_str)) > 0)) {
2730 bcopy(cp_str, buf, len);
2731 buf += len;
2732 }
2733 } else { /* Non-% character passes straight through */
2734 *buf++ = *path;
2735 }
2736 }
2737
2738 *buf = '\0';
2739 return (buf);
2740 }
2741
2742
2743 /*
2744 * Establish the module search path: state.modpath
2745 *
2746 * The path used comes from the following sources, taking the first
2747 * one that has a value, and ignoring any others:
2748 *
2749 * - ELFEDIT_PATH environment variable
2750 * - -L command line argument
2751 * - Default value
2752 *
2753 * entry:
2754 * path - NULL, or the value of the -L command line argument
2755 *
2756 * exit:
2757 * state.modpath has been filled in
2758 */
2759 static void
establish_modpath(const char * cmdline_path)2760 establish_modpath(const char *cmdline_path)
2761 {
2762 char origin_root[PATH_MAX + 1]; /* Where elfedit binary is */
2763 const char *path; /* Initial path */
2764 char *expath; /* Expanded path */
2765 size_t len;
2766 char *src, *dst;
2767
2768 path = getenv(MSG_ORIG(MSG_STR_ENVVAR));
2769 if (path == NULL)
2770 path = cmdline_path;
2771 if (path == NULL)
2772 path = MSG_ORIG(MSG_STR_MODPATH);
2773
2774
2775 /*
2776 * Root of tree containing running for running program. 32-bit elfedit
2777 * is installed in /usr/bin, and 64-bit elfedit is one level lower
2778 * in an ISA-specific subdirectory. So, we find the root by
2779 * getting the $ORGIN of the current running program, and trimming
2780 * off the last 2 (32-bit) or 3 (64-bit) directories.
2781 *
2782 * On a standard system, this will simply yield '/'. However,
2783 * doing it this way allows us to run elfedit from a proto area,
2784 * and pick up modules from the same proto area instead of those
2785 * installed on the system.
2786 */
2787 if (dlinfo(RTLD_SELF, RTLD_DI_ORIGIN, &origin_root) == -1)
2788 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTGETORIGIN));
2789 len = (sizeof (char *) == 8) ? 3 : 2;
2790 src = origin_root + strlen(origin_root);
2791 while ((src > origin_root) && (len > 0)) {
2792 if (*(src - 1) == '/')
2793 len--;
2794 src--;
2795 }
2796 *src = '\0';
2797
2798
2799 /*
2800 * Calculate space needed to hold expanded path. Note that
2801 * this assumes that MSG_STR_MODPATH will never contain a '%o'
2802 * code, and so, the expansion is not recursive. The codes allowed
2803 * are:
2804 * %i - ISA of running elfedit (sparc, sparcv9, etc)
2805 * %I - 64-bit ISA: Same as %i for 64-bit versions of elfedit,
2806 * but yields empty string for 32-bit ISAs.
2807 * %o - The original (default) path.
2808 * %r - Root of tree holding elfedit program.
2809 * %% - A single %
2810 *
2811 * A % followed by anything else is an error. This allows us to
2812 * add new codes in the future without backward compatability issues.
2813 */
2814 len = modpath_strlen(path, origin_root);
2815
2816 expath = elfedit_malloc(MSG_INTL(MSG_ALLOC_EXPATH), len + 1);
2817 (void) modpath_expand(path, origin_root, expath);
2818
2819 /*
2820 * Count path segments, eliminate extra '/', and replace ':'
2821 * with NULL.
2822 */
2823 state.modpath.n = 1;
2824 for (src = dst = expath; *src; src++) {
2825 if (*src == '/') {
2826 switch (*(src + 1)) {
2827 case '/':
2828 case ':':
2829 case '\0':
2830 continue;
2831 }
2832 }
2833 if (*src == ':') {
2834 state.modpath.n++;
2835 *dst = '\0';
2836 } else if (src != dst) {
2837 *dst = *src;
2838 }
2839 dst++;
2840 }
2841 if (src != dst)
2842 *dst = '\0';
2843
2844 state.modpath.seg = elfedit_malloc(MSG_INTL(MSG_ALLOC_PATHARR),
2845 sizeof (state.modpath.seg[0]) * state.modpath.n);
2846
2847 src = expath;
2848 for (len = 0; len < state.modpath.n; len++) {
2849 if (*src == '\0') {
2850 state.modpath.seg[len] = MSG_ORIG(MSG_STR_DOT);
2851 src++;
2852 } else {
2853 state.modpath.seg[len] = src;
2854 src += strlen(src) + 1;
2855 }
2856 }
2857 }
2858
2859 /*
2860 * When interactive (reading commands from a tty), we catch
2861 * SIGINT in order to restart the outer command loop.
2862 */
2863 /*ARGSUSED*/
2864 static void
sigint_handler(int sig,siginfo_t * sip,void * ucp)2865 sigint_handler(int sig, siginfo_t *sip, void *ucp)
2866 {
2867 /* Jump to the outer loop to resume */
2868 if (state.msg_jbuf.active) {
2869 state.msg_jbuf.active = 0;
2870 siglongjmp(state.msg_jbuf.env, 1);
2871 }
2872 }
2873
2874
2875 static void
usage(int full)2876 usage(int full)
2877 {
2878 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_BRIEF));
2879 if (full) {
2880 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL1));
2881 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL2));
2882 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL3));
2883 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL4));
2884 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL5));
2885 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL6));
2886 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL_LAST));
2887 }
2888 elfedit_exit(2);
2889 }
2890
2891
2892 /*
2893 * In order to complete commands, we need to know about them,
2894 * which means that we need to force all the modules to be
2895 * loaded. This is a relatively expensive operation, so we use
2896 * this function, which avoids doing it more than once in a session.
2897 */
2898 static void
elfedit_cpl_load_modules(void)2899 elfedit_cpl_load_modules(void)
2900 {
2901 static int loaded;
2902
2903 if (!loaded) {
2904 elfedit_load_modpath();
2905 loaded = 1; /* Don't do it again */
2906 }
2907 }
2908
2909 /*
2910 * Compare the token to the given string, and if they share a common
2911 * initial sequence, add the tail of string to the tecla command completion
2912 * buffer:
2913 *
2914 * entry:
2915 * cpldata - Current completion state
2916 * str - String to match against token
2917 * casefold - True to allow case insensitive completion, False
2918 * if case must match exactly.
2919 */
2920 void
elfedit_cpl_match(void * cpldata,const char * str,int casefold)2921 elfedit_cpl_match(void *cpldata, const char *str, int casefold)
2922 {
2923 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
2924 const char *cont_suffix;
2925 const char *type_suffix;
2926
2927 /*
2928 * Reasons to return immediately:
2929 * - NULL strings have no completion value
2930 * - The string is shorter than the existing item being completed
2931 */
2932 if ((str == NULL) || (*str == '\0') ||
2933 ((cstate->ecpl_token_len != 0) &&
2934 ((strlen(str) < cstate->ecpl_token_len))))
2935 return;
2936
2937 /* If the string does not share the existing prefix, don't use it */
2938 if (casefold) {
2939 if (strncasecmp(cstate->ecpl_token_str, str,
2940 cstate->ecpl_token_len) != 0)
2941 return;
2942 } else {
2943 if (strncmp(cstate->ecpl_token_str, str,
2944 cstate->ecpl_token_len) != 0)
2945 return;
2946 }
2947
2948 if (cstate->ecpl_add_mod_colon) {
2949 cont_suffix = type_suffix = MSG_ORIG(MSG_STR_COLON);
2950 } else {
2951 cont_suffix = MSG_ORIG(MSG_STR_SPACE);
2952 type_suffix = NULL;
2953 }
2954 (void) cpl_add_completion(cstate->ecpl_cpl, cstate->ecpl_line,
2955 cstate->ecpl_word_start, cstate->ecpl_word_end,
2956 str + cstate->ecpl_token_len, type_suffix, cont_suffix);
2957
2958 }
2959
2960
2961 /*
2962 * Convenience wrapper on elfedit_cpl_match(): Format an unsigned
2963 * 32-bit integer as a string and enter the result for command completion.
2964 */
2965 void
elfedit_cpl_ndx(void * cpldata,uint_t ndx)2966 elfedit_cpl_ndx(void *cpldata, uint_t ndx)
2967 {
2968 Conv_inv_buf_t buf;
2969
2970 (void) snprintf(buf.buf, sizeof (buf.buf),
2971 MSG_ORIG(MSG_FMT_WORDVAL), ndx);
2972 elfedit_cpl_match(cpldata, buf.buf, 0);
2973 }
2974
2975
2976 /*
2977 * Compare the token to the names of the commands from the given module,
2978 * and if they share a common initial sequence, add the tail of string
2979 * to the tecla command completion buffer:
2980 *
2981 * entry:
2982 * tok_buf - Token user has entered
2983 * tok_len - strlen(tok_buf)
2984 * mod - Module definition from which commands should be matched
2985 * cpl, line, word_start, word_end, cont_suffix - As documented
2986 * for gl_get_line() and cpl_add_completion.
2987 */
2988 static void
match_module_cmds(ELFEDIT_CPL_STATE * cstate,elfeditGC_module_t * mod)2989 match_module_cmds(ELFEDIT_CPL_STATE *cstate, elfeditGC_module_t *mod)
2990 {
2991 elfeditGC_cmd_t *cmd;
2992 const char **cmd_name;
2993
2994 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++)
2995 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++)
2996 elfedit_cpl_match(cstate, *cmd_name, 1);
2997 }
2998
2999
3000 /*
3001 * Compare the token to the known module names, and add those that
3002 * match to the list of alternatives via elfedit_cpl_match().
3003 *
3004 * entry:
3005 * load_all_modules - If True, causes all modules to be loaded
3006 * before processing is done. If False, only the modules
3007 * currently seen will be used.
3008 */
3009 void
elfedit_cpl_module(void * cpldata,int load_all_modules)3010 elfedit_cpl_module(void *cpldata, int load_all_modules)
3011 {
3012 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
3013 MODLIST_T *modlist;
3014
3015 if (load_all_modules)
3016 elfedit_cpl_load_modules();
3017
3018 for (modlist = state.modlist; modlist != NULL;
3019 modlist = modlist->ml_next) {
3020 elfedit_cpl_match(cstate, modlist->ml_mod->mod_name, 1);
3021 }
3022 }
3023
3024
3025 /*
3026 * Compare the token to all the known commands, and add those that
3027 * match to the list of alternatives.
3028 *
3029 * note:
3030 * This routine will force modules to be loaded as necessary to
3031 * obtain the names it needs to match.
3032 */
3033 void
elfedit_cpl_command(void * cpldata)3034 elfedit_cpl_command(void *cpldata)
3035 {
3036 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata;
3037 ELFEDIT_CPL_STATE colon_state;
3038 const char *colon_pos;
3039 MODLIST_T *modlist;
3040 MODLIST_T *insdef;
3041 char buf[128];
3042
3043 /*
3044 * Is there a colon in the command? If so, locate its offset within
3045 * the raw input line.
3046 */
3047 for (colon_pos = cstate->ecpl_token_str;
3048 *colon_pos && (*colon_pos != ':'); colon_pos++)
3049 ;
3050
3051 /*
3052 * If no colon was seen, then we are completing a module name,
3053 * or one of the commands from 'sys:'
3054 */
3055 if (*colon_pos == '\0') {
3056 /*
3057 * Setting cstate->add_mod_colon tells elfedit_cpl_match()
3058 * to add an implicit ':' to the names it matches. We use it
3059 * here so the user doesn't have to enter the ':' manually.
3060 * Hiding this in the opaque state instead of making it
3061 * an argument to that function gives us the ability to
3062 * change it later without breaking the published interface.
3063 */
3064 cstate->ecpl_add_mod_colon = 1;
3065 elfedit_cpl_module(cpldata, 1);
3066 cstate->ecpl_add_mod_colon = 0;
3067
3068 /* Add bare (no sys: prefix) commands from the sys: module */
3069 match_module_cmds(cstate,
3070 elfedit_load_module(MSG_ORIG(MSG_MOD_SYS), 1, 0));
3071
3072 return;
3073 }
3074
3075 /*
3076 * A colon was seen, so we have a module name. Extract the name,
3077 * substituting 'sys' for the case where the given name is empty.
3078 */
3079 if (colon_pos == 0)
3080 (void) strlcpy(buf, MSG_ORIG(MSG_MOD_SYS), sizeof (buf));
3081 else
3082 elfedit_strnbcpy(buf, cstate->ecpl_token_str,
3083 colon_pos - cstate->ecpl_token_str, sizeof (buf));
3084
3085 /*
3086 * Locate the module. If it isn't already loaded, make an explicit
3087 * attempt to load it and try again. If a module definition is
3088 * obtained, process the commands it supplies.
3089 */
3090 modlist = module_loaded(buf, &insdef);
3091 if (modlist == NULL) {
3092 (void) elfedit_load_module(buf, 0, 0);
3093 modlist = module_loaded(buf, &insdef);
3094 }
3095 if (modlist != NULL) {
3096 /*
3097 * Make a copy of the cstate, and adjust the line and
3098 * token so that the new one starts just past the colon
3099 * character. We know that the colon exists because
3100 * of the preceeding test that found it. Therefore, we do
3101 * not need to test against running off the end of the
3102 * string here.
3103 */
3104 colon_state = *cstate;
3105 while (colon_state.ecpl_line[colon_state.ecpl_word_start] !=
3106 ':')
3107 colon_state.ecpl_word_start++;
3108 while (*colon_state.ecpl_token_str != ':') {
3109 colon_state.ecpl_token_str++;
3110 colon_state.ecpl_token_len--;
3111 }
3112 /* Skip past the ':' character */
3113 colon_state.ecpl_word_start++;
3114 colon_state.ecpl_token_str++;
3115 colon_state.ecpl_token_len--;
3116
3117 match_module_cmds(&colon_state, modlist->ml_mod);
3118 }
3119 }
3120
3121
3122 /*
3123 * Command completion function for use with libtacla.
3124 */
3125 /*ARGSUSED1*/
3126 static int
cmd_match_fcn(WordCompletion * cpl,void * data,const char * line,int word_end)3127 cmd_match_fcn(WordCompletion *cpl, void *data, const char *line, int word_end)
3128 {
3129 const char *argv[ELFEDIT_MAXCPLARGS];
3130 ELFEDIT_CPL_STATE cstate;
3131 TOK_STATE *tokst;
3132 int ndx;
3133 int i;
3134 elfeditGC_module_t *mod;
3135 elfeditGC_cmd_t *cmd;
3136 int num_opt;
3137 int opt_term_seen;
3138 int skip_one;
3139 elfedit_cmd_optarg_t *optarg;
3140 elfedit_optarg_item_t item;
3141 int ostyle_ndx = -1;
3142
3143 /*
3144 * For debugging, enable the following block. It tells the tecla
3145 * library that the program using is going to write to stdout.
3146 * It will put the tty back into normal mode, and it will cause
3147 * tecla to redraw the current input line when it gets control back.
3148 */
3149 #ifdef DEBUG_CMD_MATCH
3150 gl_normal_io(state.input.gl);
3151 #endif
3152
3153 /*
3154 * Tokenize the line up through word_end. The last token in
3155 * the list is the one requiring completion.
3156 */
3157 tokst = tokenize_user_cmd(line, word_end, 1);
3158 if (tokst->tokst_cnt == 0)
3159 return (0);
3160
3161 /* Set up the cstate block, containing the completion state */
3162 ndx = tokst->tokst_cnt - 1; /* Index of token to complete */
3163 cstate.ecpl_cpl = cpl;
3164 cstate.ecpl_line = line;
3165 cstate.ecpl_word_start = tokst->tokst_buf[ndx].tok_line_off;
3166 cstate.ecpl_word_end = word_end;
3167 cstate.ecpl_add_mod_colon = 0;
3168 cstate.ecpl_token_str = tokst->tokst_buf[ndx].tok_str;
3169 cstate.ecpl_token_len = tokst->tokst_buf[ndx].tok_len;
3170
3171 /*
3172 * If there is only one token, then we are completing the
3173 * command itself.
3174 */
3175 if (ndx == 0) {
3176 elfedit_cpl_command(&cstate);
3177 return (0);
3178 }
3179
3180 /*
3181 * There is more than one token. Use the first one to
3182 * locate the definition for the command. If we don't have
3183 * a definition for the command, then there's nothing more
3184 * we can do.
3185 */
3186 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 0, &mod);
3187 if (cmd == NULL)
3188 return (0);
3189
3190 /*
3191 * Since we know the command, give them a quick usage message.
3192 * It may be that they just need a quick reminder about the form
3193 * of the command and the options.
3194 */
3195 (void) gl_normal_io(state.input.gl);
3196 elfedit_printf(MSG_INTL(MSG_USAGE_CMD),
3197 elfedit_format_command_usage(mod, cmd, NULL, 0));
3198
3199
3200 /*
3201 * We have a generous setting for ELFEDIT_MAXCPLARGS, so there
3202 * should always be plenty of room. If there's not room, we
3203 * can't proceed.
3204 */
3205 if (ndx >= ELFEDIT_MAXCPLARGS)
3206 return (0);
3207
3208 /*
3209 * Put pointers to the tokens into argv, and determine how
3210 * many of the tokens are optional arguments.
3211 *
3212 * We consider the final optional argument to be the rightmost
3213 * argument that starts with a '-'. If a '--' is seen, then
3214 * we stop there, and any argument that follows is a plain argument
3215 * (even if it starts with '-').
3216 *
3217 * We look for an inherited '-o' option, because we are willing
3218 * to supply command completion for these values.
3219 */
3220 num_opt = 0;
3221 opt_term_seen = 0;
3222 skip_one = 0;
3223 for (i = 0; i < ndx; i++) {
3224 argv[i] = tokst->tokst_buf[i + 1].tok_str;
3225 if (opt_term_seen || skip_one) {
3226 skip_one = 0;
3227 continue;
3228 }
3229 skip_one = 0;
3230 ostyle_ndx = -1;
3231 if ((strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_MINUS)) == 0) ||
3232 (*argv[i] != '-')) {
3233 opt_term_seen = 1;
3234 continue;
3235 }
3236 num_opt = i + 1;
3237 /*
3238 * If it is a recognised ELFEDIT_CMDOA_F_VALUE option,
3239 * then the item following it is the associated value.
3240 * Check for this and skip the value.
3241 *
3242 * At the same time, look for STDOA_OPT_O inherited
3243 * options. We want to identify the index of any such
3244 * item. Although the option is simply "-o", we are willing
3245 * to treat any option that starts with "-o" as a potential
3246 * STDOA_OPT_O. This lets us to command completion for things
3247 * like "-onum", and is otherwise harmless, the only cost
3248 * being a few additional strcmps by the cpl code.
3249 */
3250 if ((optarg = cmd->cmd_opt) == NULL)
3251 continue;
3252 while (optarg->oa_name != NULL) {
3253 int is_ostyle_optarg =
3254 (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) &&
3255 (optarg->oa_name == ELFEDIT_STDOA_OPT_O);
3256
3257 elfedit_next_optarg(&optarg, &item);
3258 if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE) {
3259 if (is_ostyle_optarg && (strncmp(argv[i],
3260 MSG_ORIG(MSG_STR_MINUS_O), 2) == 0))
3261 ostyle_ndx = i + 1;
3262
3263 if (strcmp(item.oai_name, argv[i]) == 0) {
3264 num_opt = i + 2;
3265 skip_one = 1;
3266 break;
3267 }
3268 /*
3269 * If it didn't match "-o" exactly, but it is
3270 * ostyle_ndx, then it is a potential combined
3271 * STDOA_OPT_O, as discussed above. It counts
3272 * as a single argument.
3273 */
3274 if (ostyle_ndx == ndx)
3275 break;
3276 }
3277 }
3278 }
3279
3280 #ifdef DEBUG_CMD_MATCH
3281 (void) printf("NDX(%d) NUM_OPT(%d) ostyle_ndx(%d)\n", ndx, num_opt,
3282 ostyle_ndx);
3283 #endif
3284
3285 if (ostyle_ndx != -1) {
3286 /*
3287 * If ostyle_ndx is one less than ndx, and ndx is
3288 * the same as num_opt, then we have a definitive
3289 * STDOA_OPT_O inherited outstyle option. We supply
3290 * the value strings, and are done.
3291 */
3292 if ((ostyle_ndx == (ndx - 1)) && (ndx == num_opt)) {
3293 elfedit_cpl_atoconst(&cstate, ELFEDIT_CONST_OUTSTYLE);
3294 return (0);
3295 }
3296
3297 /*
3298 * If ostyle is the same as ndx, then we have an option
3299 * staring with "-o" that may end up being a STDOA_OPT_O,
3300 * and we are still inside that token. In this case, we
3301 * supply completion strings that include the leading
3302 * "-o" followed by the values, without a space
3303 * (i.e. "-onum"). We then fall through, allowing any
3304 * other options starting with "-o" to be added
3305 * below. elfedit_cpl_match() will throw out the incorrect
3306 * options, so it is harmless to add these extra items in
3307 * the worst case, and useful otherwise.
3308 */
3309 if (ostyle_ndx == ndx)
3310 elfedit_cpl_atoconst(&cstate,
3311 ELFEDIT_CONST_OUTSTYLE_MO);
3312 }
3313
3314 /*
3315 * If (ndx <= num_opt), then the token needing completion
3316 * is an option. If the leading '-' is there, then we should fill
3317 * in all of the option alternatives. If anything follows the '-'
3318 * though, we assume that the user has already figured out what
3319 * option to use, and we leave well enough alone.
3320 *
3321 * Note that we are intentionally ignoring a related case
3322 * where supplying option strings would be legal: In the case
3323 * where we are one past the last option (ndx == (num_opt + 1)),
3324 * and the current option is an empty string, the argument can
3325 * be either a plain argument or an option --- the user needs to
3326 * enter the next character before we can tell. It would be
3327 * OK to enter the option strings in this case. However, consider
3328 * what happens when the first plain argument to the command does
3329 * not provide any command completion (e.g. it is a plain integer).
3330 * In this case, tecla will see that all the alternatives start
3331 * with '-', and will insert a '-' into the input. If the user
3332 * intends the next argument to be plain, they will have to delete
3333 * this '-', which is annoying. Worse than that, they may be confused
3334 * by it, and think that the plain argument is not allowed there.
3335 * The best solution is to not supply option strings unless the
3336 * user first enters the '-'.
3337 */
3338 if ((ndx <= num_opt) && (argv[ndx - 1][0] == '-')) {
3339 if ((optarg = cmd->cmd_opt) != NULL) {
3340 while (optarg->oa_name != NULL) {
3341 elfedit_next_optarg(&optarg, &item);
3342 elfedit_cpl_match(&cstate, item.oai_name, 1);
3343 }
3344 }
3345 return (0);
3346 }
3347
3348 /*
3349 * At this point we know that ndx and num_opt are not equal.
3350 * If num_opt is larger than ndx, then we have an ELFEDIT_CMDOA_F_VALUE
3351 * argument at the end, and the following value has not been entered.
3352 *
3353 * If ndx is greater than num_opt, it means that we are looking
3354 * at a plain argument (or in the case where (ndx == (num_opt + 1)),
3355 * a *potential* plain argument.
3356 *
3357 * If the command has a completion function registered, then we
3358 * hand off the remaining work to it. The cmd_cplfunc field is
3359 * the generic definition. We need to cast it to the type that matches
3360 * the proper ELFCLASS before calling it.
3361 */
3362 if (state.elf.elfclass == ELFCLASS32) {
3363 elfedit32_cmdcpl_func_t *cmdcpl_func =
3364 (elfedit32_cmdcpl_func_t *)cmd->cmd_cplfunc;
3365
3366 if (cmdcpl_func != NULL)
3367 (* cmdcpl_func)(state.elf.obj_state.s32,
3368 &cstate, ndx, argv, num_opt);
3369 } else {
3370 elfedit64_cmdcpl_func_t *cmdcpl_func =
3371 (elfedit64_cmdcpl_func_t *)cmd->cmd_cplfunc;
3372
3373 if (cmdcpl_func != NULL)
3374 (* cmdcpl_func)(state.elf.obj_state.s64,
3375 &cstate, ndx, argv, num_opt);
3376 }
3377
3378 return (0);
3379 }
3380
3381
3382 /*
3383 * Read a line of input from stdin, and return pointer to it.
3384 *
3385 * This routine uses a private buffer, so the contents of the returned
3386 * string are only good until the next call.
3387 */
3388 static const char *
read_cmd(void)3389 read_cmd(void)
3390 {
3391 char *s;
3392
3393 if (state.input.full_tty) {
3394 state.input.in_tecla = TRUE;
3395 s = gl_get_line(state.input.gl,
3396 MSG_ORIG(MSG_STR_PROMPT), NULL, -1);
3397 state.input.in_tecla = FALSE;
3398 /*
3399 * gl_get_line() returns NULL for EOF or for error. EOF is fine,
3400 * but we need to catch and report anything else. Since
3401 * reading from stdin is critical to our operation, an
3402 * error implies that we cannot recover and must exit.
3403 */
3404 if ((s == NULL) &&
3405 (gl_return_status(state.input.gl) == GLR_ERROR)) {
3406 elfedit_msg(ELFEDIT_MSG_FATAL, MSG_INTL(MSG_ERR_GLREAD),
3407 gl_error_message(state.input.gl, NULL, 0));
3408 }
3409 } else {
3410 /*
3411 * This should be a dynamically sized buffer, but for now,
3412 * I'm going to take a simpler path.
3413 */
3414 static char cmd_buf[ELFEDIT_MAXCMD + 1];
3415
3416 s = fgets(cmd_buf, sizeof (cmd_buf), stdin);
3417 }
3418
3419 /* Return user string, or 'quit' on EOF */
3420 return (s ? s : MSG_ORIG(MSG_SYS_CMD_QUIT));
3421 }
3422
3423 int
main(int argc,char ** argv,char ** envp)3424 main(int argc, char **argv, char **envp)
3425 {
3426 /*
3427 * Note: This function can use setjmp()/longjmp() which does
3428 * not preserve the values of auto/register variables. Hence,
3429 * variables that need their values preserved across a jump must
3430 * be marked volatile, or must not be auto/register.
3431 *
3432 * Volatile can be messy, because it requires explictly casting
3433 * away the attribute when passing it to functions, or declaring
3434 * those functions with the attribute as well. In a single threaded
3435 * program like this one, an easier approach is to make things
3436 * static. That can be done here, or by putting things in the
3437 * 'state' structure.
3438 */
3439
3440 int c, i;
3441 int num_batch = 0;
3442 char **batch_list = NULL;
3443 const char *modpath = NULL;
3444
3445 /*
3446 * Always have liblddb display unclipped section names.
3447 * This global is exported by liblddb, and declared in debug.h.
3448 */
3449 dbg_desc->d_extra |= DBG_E_LONG;
3450
3451 opterr = 0;
3452 while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
3453 switch (c) {
3454 case 'a':
3455 state.flags |= ELFEDIT_F_AUTOPRINT;
3456 break;
3457
3458 case 'd':
3459 state.flags |= ELFEDIT_F_DEBUG;
3460 break;
3461
3462 case 'e':
3463 /*
3464 * Delay parsing the -e options until after the call to
3465 * conv_check_native() so that we won't bother loading
3466 * modules of the wrong class.
3467 */
3468 if (batch_list == NULL)
3469 batch_list = elfedit_malloc(
3470 MSG_INTL(MSG_ALLOC_BATCHLST),
3471 sizeof (*batch_list) * (argc - 1));
3472 batch_list[num_batch++] = optarg;
3473 break;
3474
3475 case 'L':
3476 modpath = optarg;
3477 break;
3478
3479 case 'o':
3480 if (elfedit_atooutstyle(optarg, &state.outstyle) == 0)
3481 usage(1);
3482 break;
3483
3484 case 'r':
3485 state.flags |= ELFEDIT_F_READONLY;
3486 break;
3487
3488 case '?':
3489 usage(1);
3490 }
3491 }
3492
3493 /*
3494 * We allow 0, 1, or 2 files:
3495 *
3496 * The no-file case is an extremely limited mode, in which the
3497 * only commands allowed to execute come from the sys: module.
3498 * This mode exists primarily to allow easy access to the help
3499 * facility.
3500 *
3501 * To get full access to elfedit's capablities, there must
3502 * be an input file. If this is not a readonly
3503 * session, then an optional second output file is allowed.
3504 *
3505 * In the case where two files are given and the session is
3506 * readonly, use a full usage message, because the simple
3507 * one isn't enough for the user to understand their error.
3508 * Otherwise, the simple usage message suffices.
3509 */
3510 argc = argc - optind;
3511 if ((argc == 2) && (state.flags & ELFEDIT_F_READONLY))
3512 usage(1);
3513 if (argc > 2)
3514 usage(0);
3515
3516 state.file.present = (argc != 0);
3517
3518 /*
3519 * If we have a file to edit, and unless told otherwise by the
3520 * caller, we try to run the 64-bit version of this program
3521 * when the system is capable of it. If that fails, then we
3522 * continue on with the currently running version.
3523 *
3524 * To force 32-bit execution on a 64-bit host, set the
3525 * LD_NOEXEC_64 environment variable to a non-empty value.
3526 *
3527 * There is no reason to bother with this if in "no file" mode.
3528 */
3529 if (state.file.present != 0)
3530 (void) conv_check_native(argv, envp);
3531
3532 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_VERSION),
3533 (sizeof (char *) == 8) ? 64 : 32);
3534
3535 /*
3536 * Put a module definition for the builtin system module on the
3537 * module list. We know it starts out empty, so we do not have
3538 * to go through a more general insertion process than this.
3539 */
3540 state.modlist = elfedit_sys_init(ELFEDIT_VER_CURRENT);
3541
3542 /* Establish the search path for loadable modules */
3543 establish_modpath(modpath);
3544
3545 /*
3546 * Now that we are running the final version of this program,
3547 * deal with the input/output file(s).
3548 */
3549 if (state.file.present == 0) {
3550 /*
3551 * This is arbitrary --- we simply need to be able to
3552 * load modules so that we can access their help strings
3553 * and command completion functions. Without a file, we
3554 * will refuse to call commands from any module other
3555 * than sys. Those commands have been written to be aware
3556 * of the case where there is no input file, and are
3557 * therefore safe to run.
3558 */
3559 state.elf.elfclass = ELFCLASS32;
3560 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NOFILE));
3561
3562 } else {
3563 state.file.infile = argv[optind];
3564 if (argc == 1) {
3565 state.file.outfile = state.file.infile;
3566 if (state.flags & ELFEDIT_F_READONLY)
3567 elfedit_msg(ELFEDIT_MSG_DEBUG,
3568 MSG_INTL(MSG_DEBUG_READONLY));
3569 else
3570 elfedit_msg(ELFEDIT_MSG_DEBUG,
3571 MSG_INTL(MSG_DEBUG_INPLACEWARN),
3572 state.file.infile);
3573 } else {
3574 state.file.outfile = argv[optind + 1];
3575 create_outfile(state.file.infile, state.file.outfile);
3576 elfedit_msg(ELFEDIT_MSG_DEBUG,
3577 MSG_INTL(MSG_DEBUG_CPFILE),
3578 state.file.infile, state.file.outfile);
3579 /*
3580 * We are editing a copy of the original file that we
3581 * just created. If we should exit before the edits are
3582 * updated, then we want to unlink this copy so that we
3583 * don't leave junk lying around. Once an update
3584 * succeeds however, we'll leave it in place even
3585 * if an error occurs afterwards.
3586 */
3587 state.file.unlink_on_exit = 1;
3588 optind++; /* Edit copy instead of the original */
3589 }
3590
3591 init_obj_state(state.file.outfile);
3592 }
3593
3594
3595 /*
3596 * Process commands.
3597 *
3598 * If any -e options were used, then do them and
3599 * immediately exit. On error, exit immediately without
3600 * updating the target ELF file. On success, the 'write'
3601 * and 'quit' commands are implicit in this mode.
3602 *
3603 * If no -e options are used, read commands from stdin.
3604 * quit must be explicitly used. Exit is implicit on EOF.
3605 * If stdin is a tty, then errors do not cause the editor
3606 * to terminate. Rather, the error message is printed, and the
3607 * user prompted to continue.
3608 */
3609 if (batch_list != NULL) { /* -e was used */
3610 /* Compile the commands */
3611 for (i = 0; i < num_batch; i++)
3612 parse_user_cmd(batch_list[i]);
3613 free(batch_list);
3614
3615 /*
3616 * 'write' and 'quit' are implicit in this mode.
3617 * Add them as well.
3618 */
3619 if ((state.flags & ELFEDIT_F_READONLY) == 0)
3620 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_WRITE));
3621 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_QUIT));
3622
3623 /* And run them. This won't return, thanks to the 'quit' */
3624 dispatch_user_cmds();
3625 } else {
3626 state.input.is_tty = isatty(fileno(stdin));
3627 state.input.full_tty = state.input.is_tty &&
3628 isatty(fileno(stdout));
3629
3630 if (state.input.full_tty) {
3631 struct sigaction act;
3632
3633 act.sa_sigaction = sigint_handler;
3634 (void) sigemptyset(&act.sa_mask);
3635 act.sa_flags = 0;
3636 if (sigaction(SIGINT, &act, NULL) == -1) {
3637 int err = errno;
3638 elfedit_msg(ELFEDIT_MSG_ERR,
3639 MSG_INTL(MSG_ERR_SIGACTION), strerror(err));
3640 }
3641 /*
3642 * If pager process exits before we are done
3643 * writing, we can see SIGPIPE. Prevent it
3644 * from killing the process.
3645 */
3646 (void) sigignore(SIGPIPE);
3647
3648 /* Open tecla handle for command line editing */
3649 state.input.gl = new_GetLine(ELFEDIT_MAXCMD,
3650 ELFEDIT_MAXHIST);
3651 /* Register our command completion function */
3652 (void) gl_customize_completion(state.input.gl,
3653 NULL, cmd_match_fcn);
3654
3655 /*
3656 * Make autoprint the default for interactive
3657 * sessions.
3658 */
3659 state.flags |= ELFEDIT_F_AUTOPRINT;
3660 }
3661 for (;;) {
3662 /*
3663 * If this is an interactive session, then use
3664 * sigsetjmp()/siglongjmp() to recover from bad
3665 * commands and keep going. A non-0 return from
3666 * sigsetjmp() means that an error just occurred.
3667 * In that case, we simply restart this loop.
3668 */
3669 if (state.input.is_tty) {
3670 if (sigsetjmp(state.msg_jbuf.env, 1) != 0) {
3671 if (state.input.full_tty)
3672 gl_abandon_line(state.input.gl);
3673 continue;
3674 }
3675 state.msg_jbuf.active = TRUE;
3676 }
3677
3678 /*
3679 * Force all output out before each command.
3680 * This is a no-OP when a tty is in use, but
3681 * in a pipeline, it ensures that the block
3682 * mode buffering doesn't delay output past
3683 * the completion of each command.
3684 *
3685 * If we didn't do this, the output would eventually
3686 * arrive at its destination, but the lag can be
3687 * annoying when you pipe the output into a tool
3688 * that displays the results in real time.
3689 */
3690 (void) fflush(stdout);
3691 (void) fflush(stderr);
3692
3693 parse_user_cmd(read_cmd());
3694 dispatch_user_cmds();
3695 state.msg_jbuf.active = FALSE;
3696 }
3697 }
3698
3699
3700 /*NOTREACHED*/
3701 return (0);
3702 }
3703