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