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