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