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