15088Sab196087 /* 25088Sab196087 * CDDL HEADER START 35088Sab196087 * 45088Sab196087 * The contents of this file are subject to the terms of the 55088Sab196087 * Common Development and Distribution License (the "License"). 65088Sab196087 * You may not use this file except in compliance with the License. 75088Sab196087 * 85088Sab196087 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95088Sab196087 * or http://www.opensolaris.org/os/licensing. 105088Sab196087 * See the License for the specific language governing permissions 115088Sab196087 * and limitations under the License. 125088Sab196087 * 135088Sab196087 * When distributing Covered Code, include this CDDL HEADER in each 145088Sab196087 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155088Sab196087 * If applicable, add the following below this CDDL HEADER, with the 165088Sab196087 * fields enclosed by brackets "[]" replaced with your own identifying 175088Sab196087 * information: Portions Copyright [yyyy] [name of copyright owner] 185088Sab196087 * 195088Sab196087 * CDDL HEADER END 205088Sab196087 */ 215088Sab196087 225088Sab196087 /* 235892Sab196087 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 245088Sab196087 * Use is subject to license terms. 255088Sab196087 */ 265088Sab196087 #pragma ident "%Z%%M% %I% %E% SMI" 275088Sab196087 285088Sab196087 #include <sys/types.h> 295088Sab196087 #include <sys/stat.h> 305088Sab196087 #include <sys/wait.h> 315088Sab196087 #include <stdarg.h> 325088Sab196087 #include <fcntl.h> 335088Sab196087 #include <stdlib.h> 345088Sab196087 #include <stdio.h> 355088Sab196087 #include <signal.h> 365088Sab196087 #include <dirent.h> 375088Sab196087 #include <libelf.h> 385088Sab196087 #include <gelf.h> 395088Sab196087 #include <conv.h> 405088Sab196087 #include <dlfcn.h> 415088Sab196087 #include <link.h> 425088Sab196087 #include <stdarg.h> 435088Sab196087 #include <libgen.h> 445088Sab196087 #include <libintl.h> 455088Sab196087 #include <locale.h> 465088Sab196087 #include <unistd.h> 475088Sab196087 #include <errno.h> 485088Sab196087 #include <ctype.h> 495088Sab196087 #include <limits.h> 505088Sab196087 #include <strings.h> 515088Sab196087 #include <sgs.h> 525088Sab196087 #include "msg.h" 535088Sab196087 #include "_elfedit.h" 545088Sab196087 #include <debug.h> /* liblddb */ 555088Sab196087 565088Sab196087 575088Sab196087 585088Sab196087 /* 595088Sab196087 * Column at which elfedit_format_command_usage() will wrap the 605088Sab196087 * generated usage string if the wrap argument is True (1). 615088Sab196087 */ 625088Sab196087 #define USAGE_WRAP_COL 55 635088Sab196087 645088Sab196087 655088Sab196087 665088Sab196087 675088Sab196087 /* 685088Sab196087 * Type used to represent a string buffer that can grow as needed 695088Sab196087 * to hold strings of arbitrary length. The user should declare 705088Sab196087 * variables of this type sa static. The strbuf_ensure_size() function 715088Sab196087 * is used to ensure that it has a minimum desired size. 725088Sab196087 */ 735088Sab196087 typedef struct { 745088Sab196087 char *buf; /* String buffer */ 755088Sab196087 size_t n; /* Size of buffer */ 765088Sab196087 } STRBUF; 775088Sab196087 785088Sab196087 795088Sab196087 805088Sab196087 815088Sab196087 /* 825088Sab196087 * Types used by tokenize_user_cmd() to represent the result of 835088Sab196087 * spliting a user command into individual tokens. 845088Sab196087 */ 855088Sab196087 typedef struct { 865088Sab196087 char *tok_str; /* Token string */ 875088Sab196087 size_t tok_len; /* strlen(str) */ 885088Sab196087 size_t tok_line_off; /* Token offset in original string */ 895088Sab196087 } TOK_ELT; 905088Sab196087 typedef struct { 915088Sab196087 size_t tokst_cmd_len; /* Length of original user command, without */ 925088Sab196087 /* newline or NULL termination chars */ 935088Sab196087 size_t tokst_str_size; /* Space needed to hold all the resulting */ 945088Sab196087 /* tokens, including terminating NULL */ 955088Sab196087 TOK_ELT *tokst_buf; /* The array of tokens */ 965088Sab196087 size_t tokst_cnt; /* # of tokens in array */ 975088Sab196087 size_t tokst_bufsize; /* capacity of array */ 985088Sab196087 } TOK_STATE; 995088Sab196087 1005088Sab196087 1015088Sab196087 1025088Sab196087 1035088Sab196087 /* State block used by gettok_init() and gettok() */ 1045088Sab196087 typedef struct { 1055088Sab196087 const char *gtok_buf; /* Addr of buffer containing string */ 1065088Sab196087 char *gtok_cur_buf; /* Addr withing buffer for next token */ 1075088Sab196087 int gtok_inc_null_final; /* True if final NULL token used */ 1085088Sab196087 int gtok_null_seen; /* True when NULL byte seen */ 1095088Sab196087 TOK_ELT gtok_last_token; /* Last token parsed */ 1105088Sab196087 1115088Sab196087 } GETTOK_STATE; 1125088Sab196087 1135088Sab196087 1145088Sab196087 1155088Sab196087 1165088Sab196087 /* 1175088Sab196087 * The elfedit_cpl_*() functions are used for command line completion. 1185088Sab196087 * Currently this uses the tecla library, but to allow for changing the 1195088Sab196087 * library used, we hide all tecla interfaces from our modules. Instead, 1205088Sab196087 * cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the 1215088Sab196087 * address of that struct as an opaque handle to the modules. Since the 1225088Sab196087 * pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change 1235088Sab196087 * as necessary. 1245088Sab196087 */ 1255088Sab196087 typedef struct { 1265088Sab196087 WordCompletion *ecpl_cpl; /* tecla handle */ 1275088Sab196087 const char *ecpl_line; /* raw input line */ 1285088Sab196087 int ecpl_word_start; /* start offset within line */ 1295088Sab196087 int ecpl_word_end; /* offset just past token */ 1305088Sab196087 /* 1315088Sab196087 * ecpl_add_mod_colon is a secret handshake between 1325088Sab196087 * elfedit_cpl_command() and elfedit_cpl_add_match(). It adds 1335088Sab196087 * ':' to end of matched modules. 1345088Sab196087 */ 1355088Sab196087 int ecpl_add_mod_colon; 1365088Sab196087 const char *ecpl_token_str; /* token being completed */ 1375088Sab196087 size_t ecpl_token_len; /* strlen(ecpl_token_str) */ 1385088Sab196087 } ELFEDIT_CPL_STATE; 1395088Sab196087 1405088Sab196087 1415088Sab196087 1425088Sab196087 1435088Sab196087 /* This structure maintains elfedit global state */ 1445088Sab196087 STATE_T state; 1455088Sab196087 1465088Sab196087 1475088Sab196087 1485088Sab196087 /* 1495088Sab196087 * Define a pair of static global variables that contain the 1505088Sab196087 * ISA strings that correspond to %i and %I tokens in module search 1515088Sab196087 * paths. 1525088Sab196087 * 1535088Sab196087 * isa_i_str - The ISA string for the currently running program 1545088Sab196087 * isa_I_str - For 64-bit programs, the same as isa_i_str. For 1555088Sab196087 * 32-bit programs, an empty string. 1565088Sab196087 */ 1575088Sab196087 #ifdef __sparc 1585088Sab196087 #ifdef __sparcv9 1595088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_64); 1605088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_SPARC_64); 1615088Sab196087 #else 1625088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_32); 1635088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY); 1645088Sab196087 #endif 1655088Sab196087 #endif 1665088Sab196087 1675088Sab196087 #ifdef __i386 1685088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_32); 1695088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY); 1705088Sab196087 #endif 1715088Sab196087 #ifdef __amd64 1725088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_64); 1735088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_X86_64); 1745088Sab196087 #endif 1755088Sab196087 1765088Sab196087 1775088Sab196087 1785088Sab196087 /* Forward declarations */ 1795088Sab196087 static void free_user_cmds(void); 1805088Sab196087 static void elfedit_pager_cleanup(void); 1815088Sab196087 1825088Sab196087 1835088Sab196087 1845088Sab196087 /* 1855088Sab196087 * We supply this function for the msg module 1865088Sab196087 */ 1875088Sab196087 const char * 1885088Sab196087 _elfedit_msg(Msg mid) 1895088Sab196087 { 1905088Sab196087 return (gettext(MSG_ORIG(mid))); 1915088Sab196087 } 1925088Sab196087 1935088Sab196087 1945088Sab196087 /* 1955088Sab196087 * Copy at most min(cpsize, dstsize-1) bytes from src into dst, 1965088Sab196087 * truncating src if necessary. The result is always null-terminated. 1975088Sab196087 * 1985088Sab196087 * entry: 1995088Sab196087 * dst - Destination buffer 2005088Sab196087 * src - Source string 2015088Sab196087 * dstsize - sizeof(dst) 2025088Sab196087 * 2035088Sab196087 * note: 2045088Sab196087 * This is similar to strncpy(), but with two modifications: 2055088Sab196087 * 1) You specify the number of characters to copy, not just 2065088Sab196087 * the size of the destination. Hence, you can copy non-NULL 2075088Sab196087 * terminated strings. 2085088Sab196087 * 2) The destination is guaranteed to be NULL terminated. strncpy() 2095088Sab196087 * does not terminate a completely full buffer. 2105088Sab196087 */ 2115088Sab196087 static void 2125088Sab196087 elfedit_strnbcpy(char *dst, const char *src, size_t cpsize, size_t dstsize) 2135088Sab196087 { 2145088Sab196087 if (cpsize >= dstsize) 2155088Sab196087 cpsize = dstsize - 1; 2165088Sab196087 if (cpsize > 0) 2175088Sab196087 (void) strncpy(dst, src, cpsize + 1); 2185088Sab196087 dst[cpsize] = '\0'; 2195088Sab196087 } 2205088Sab196087 2215088Sab196087 2225088Sab196087 /* 2235088Sab196087 * Calls exit() on behalf of elfedit. 2245088Sab196087 */ 2255088Sab196087 void 2265088Sab196087 elfedit_exit(int status) 2275088Sab196087 { 2285088Sab196087 if (state.file.present) { 2295088Sab196087 /* Exiting with unflushed changes pending? Issue debug notice */ 2305088Sab196087 if (state.file.dirty) 2315088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 2325088Sab196087 MSG_INTL(MSG_DEBUG_DIRTYEXIT)); 2335088Sab196087 2345088Sab196087 /* 2355088Sab196087 * If the edit file is marked for unlink on exit, then 2365088Sab196087 * take care of it here. 2375088Sab196087 */ 2385088Sab196087 if (state.file.unlink_on_exit) { 2395088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 2405088Sab196087 MSG_INTL(MSG_DEBUG_UNLINKFILE), 2415088Sab196087 state.file.outfile); 2425088Sab196087 (void) unlink(state.file.outfile); 2435088Sab196087 } 2445088Sab196087 } 2455088Sab196087 2465088Sab196087 exit(status); 2475088Sab196087 } 2485088Sab196087 2495088Sab196087 2505088Sab196087 /* 2515088Sab196087 * Standard message function for elfedit. All user visible 2525088Sab196087 * output, for error or informational reasons, should go through 2535088Sab196087 * this function. 2545088Sab196087 * 2555088Sab196087 * entry: 2565088Sab196087 * type - Type of message. One of the ELFEDIT_MSG_* values. 2575088Sab196087 * format, ... - As per the printf() family 2585088Sab196087 * 2595088Sab196087 * exit: 2605088Sab196087 * The desired message has been output. For informational 2615088Sab196087 * messages, control returns to the caller. For errors, 2625088Sab196087 * this routine will terminate execution or strip the execution 2635088Sab196087 * stack and return control directly to the outer control loop. 2645088Sab196087 * In either case, the caller will not receive control. 2655088Sab196087 */ 2665088Sab196087 /*PRINTFLIKE2*/ 2675088Sab196087 void 2685088Sab196087 elfedit_msg(elfedit_msg_t type, const char *format, ...) 2695088Sab196087 { 2705088Sab196087 typedef enum { /* What to do after finished */ 2715088Sab196087 DISP_RET = 0, /* Return to caller */ 2725088Sab196087 DISP_JMP = 1, /* if (interactive) longjmp else exit */ 2735088Sab196087 DISP_EXIT = 2 /* exit under all circumstances */ 2745088Sab196087 } DISP; 2755088Sab196087 2765088Sab196087 va_list args; 2775088Sab196087 FILE *stream = stderr; 2785088Sab196087 DISP disp = DISP_RET; 2795088Sab196087 int do_output = 1; 2805088Sab196087 int need_prefix = 1; 2815088Sab196087 2825088Sab196087 va_start(args, format); 2835088Sab196087 2845088Sab196087 switch (type) { 2855088Sab196087 case ELFEDIT_MSG_ERR: 2865088Sab196087 case ELFEDIT_MSG_CMDUSAGE: 2875088Sab196087 disp = DISP_JMP; 2885088Sab196087 break; 2895088Sab196087 case ELFEDIT_MSG_FATAL: 2905088Sab196087 disp = DISP_EXIT; 2915088Sab196087 break; 2925088Sab196087 case ELFEDIT_MSG_USAGE: 2935088Sab196087 need_prefix = 0; 2945088Sab196087 break; 2955088Sab196087 case ELFEDIT_MSG_DEBUG: 2965088Sab196087 if (!(state.flags & ELFEDIT_F_DEBUG)) 2975088Sab196087 return; 2985088Sab196087 stream = stdout; 2995088Sab196087 break; 3005088Sab196087 case ELFEDIT_MSG_QUIET: 3015088Sab196087 do_output = 0; 3025088Sab196087 disp = DISP_JMP; 3035088Sab196087 break; 3045088Sab196087 } 3055088Sab196087 3065088Sab196087 3075088Sab196087 /* 3085088Sab196087 * If there is a pager process running, we are returning to the 3095088Sab196087 * caller, and the output is going to stdout, then let the 3105088Sab196087 * pager handle it instead of writing it directly from this process. 3115088Sab196087 * That way, the output gets paged along with everything else. 3125088Sab196087 * 3135088Sab196087 * If there is a pager process running, and we are not returning 3145088Sab196087 * to the caller, then end the pager process now, before we generate 3155088Sab196087 * any new output. This allows for any text buffered in the pager 3165088Sab196087 * pipe to be output before the new stuff. 3175088Sab196087 */ 3185088Sab196087 if (state.pager.fptr != NULL) { 3195088Sab196087 if (disp == DISP_RET) { 3205088Sab196087 if (stream == stdout) 3215088Sab196087 stream = state.pager.fptr; 3225088Sab196087 } else { 3235088Sab196087 elfedit_pager_cleanup(); 3245088Sab196087 } 3255088Sab196087 } 3265088Sab196087 3275088Sab196087 /* 3285088Sab196087 * If this message is coming from within the libtecla command 3295088Sab196087 * completion code, call gl_normal_io() to give the library notice. 3305088Sab196087 * That function sets the tty back to cooked mode and advances 3315088Sab196087 * the cursor to the beginning of the next line so that our output 3325088Sab196087 * will appear properly. When we return to the command completion code, 3335088Sab196087 * tecla will re-enter raw mode and redraw the current command line. 3345088Sab196087 */ 3355088Sab196087 if (state.input.in_tecla) 3365088Sab196087 (void) gl_normal_io(state.input.gl); 3375088Sab196087 3385088Sab196087 if (do_output) { 3395088Sab196087 if (need_prefix) 3405088Sab196087 (void) fprintf(stream, MSG_ORIG(MSG_STR_ELFEDIT)); 3415088Sab196087 (void) vfprintf(stream, format, args); 3425088Sab196087 (void) fflush(stream); 3435088Sab196087 } 3445088Sab196087 va_end(args); 3455088Sab196087 3465088Sab196087 /* 3475088Sab196087 * If this is an error, then we do not return to the caller. 3485088Sab196087 * The action taken depends on whether the outer loop has registered 3495088Sab196087 * a jump buffer for us or not. 3505088Sab196087 */ 3515088Sab196087 if (disp != DISP_RET) { 3525088Sab196087 if (state.msg_jbuf.active && (disp == DISP_JMP)) { 3535088Sab196087 /* Free the user command list */ 3545088Sab196087 free_user_cmds(); 3555088Sab196087 3565088Sab196087 /* Clean up to reflect effect of non-local goto */ 3575088Sab196087 state.input.in_tecla = FALSE; 3585088Sab196087 3595088Sab196087 /* Jump to the outer loop to resume */ 3605088Sab196087 siglongjmp(state.msg_jbuf.env, 1); 3615088Sab196087 } else { 3625088Sab196087 elfedit_exit(1); 3635088Sab196087 } 3645088Sab196087 } 3655088Sab196087 } 3665088Sab196087 3675088Sab196087 3685088Sab196087 /* 3695088Sab196087 * Wrapper on elfedit_msg() that issues an error that results from 3705088Sab196087 * a call to libelf. 3715088Sab196087 * 3725088Sab196087 * entry: 3735088Sab196087 * file - Name of ELF object 3745088Sab196087 * libelf_rtn_name - Name of routine that was called 3755088Sab196087 * 3765088Sab196087 * exit: 3775088Sab196087 * An error has been issued that shows the routine called 3785088Sab196087 * and the libelf error string for it from elf_errmsg(). 3795088Sab196087 * This routine does not return to the caller. 3805088Sab196087 */ 3815088Sab196087 void 3825088Sab196087 elfedit_elferr(const char *file, const char *libelf_rtn_name) 3835088Sab196087 { 3845088Sab196087 const char *errstr = elf_errmsg(elf_errno()); 3855088Sab196087 3865088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF), file, 3875088Sab196087 libelf_rtn_name, errstr ? errstr : MSG_INTL(MSG_FMT_UNKNOWN)); 3885088Sab196087 } 3895088Sab196087 3905088Sab196087 3915088Sab196087 /* 3925088Sab196087 * Start an output pager process for elfedit_printf()/elfedit_write() to use. 3935088Sab196087 * 3945088Sab196087 * note: 3955088Sab196087 * If this elfedit session is not interactive, then no pager is 3965088Sab196087 * started. Paging is only intended for interactive use. The caller 3975088Sab196087 * is not supposed to worry about this point, but simply to use 3985088Sab196087 * this function to flag situations in which paging might be needed. 3995088Sab196087 */ 4005088Sab196087 void 4015088Sab196087 elfedit_pager_init(void) 4025088Sab196087 { 4035088Sab196087 const char *errstr; 4045088Sab196087 const char *cmd; 4055088Sab196087 int err; 4065088Sab196087 4075088Sab196087 /* 4085088Sab196087 * If there is no pager process running, start one. 4095088Sab196087 * Only do this for interactive sessions --- elfedit_pager() 4105088Sab196087 * won't use a pager in batch mode. 4115088Sab196087 */ 4125088Sab196087 if (state.msg_jbuf.active && state.input.full_tty && 4135088Sab196087 (state.pager.fptr == NULL)) { 4145088Sab196087 /* 4155088Sab196087 * If the user has the PAGER environment variable set, 4165088Sab196087 * then we will use that program. Otherwise we default 4175088Sab196087 * to /bin/more. 4185088Sab196087 */ 4195088Sab196087 cmd = getenv(MSG_ORIG(MSG_STR_PAGER)); 4205088Sab196087 if ((cmd == NULL) || (*cmd == '\0')) 4215088Sab196087 cmd = MSG_ORIG(MSG_STR_BINMORE); 4225088Sab196087 4235088Sab196087 /* 4245088Sab196087 * The popen() manpage says that on failure, it "may set errno", 4255088Sab196087 * which is somewhat ambiguous. We explicitly zero it here, and 4265088Sab196087 * assume that any change is due to popen() failing. 4275088Sab196087 */ 4285088Sab196087 errno = 0; 4295088Sab196087 state.pager.fptr = popen(cmd, MSG_ORIG(MSG_STR_W)); 4305088Sab196087 if (state.pager.fptr == NULL) { 4315088Sab196087 err = errno; 4325088Sab196087 errstr = (err == 0) ? MSG_INTL(MSG_ERR_UNKNOWNSYSERR) : 4335088Sab196087 strerror(err); 4345088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTEXEC), 4355088Sab196087 MSG_ORIG(MSG_STR_ELFEDIT), cmd, errstr); 4365088Sab196087 } 4375088Sab196087 } 4385088Sab196087 } 4395088Sab196087 4405088Sab196087 4415088Sab196087 /* 4425088Sab196087 * If there is a pager process present, close it out. 4435088Sab196087 * 4445088Sab196087 * note: 4455088Sab196087 * This function is called from within elfedit_msg(), and as 4465088Sab196087 * such, must not use elfedit_msg() to report errors. Furthermore, 4475088Sab196087 * any such errors are not a sufficient reason to terminate the process 4485088Sab196087 * or to longjmp(). This is a rare case where errors are written 4495088Sab196087 * directly to stderr. 4505088Sab196087 */ 4515088Sab196087 static void 4525088Sab196087 elfedit_pager_cleanup(void) 4535088Sab196087 { 4545088Sab196087 if (state.pager.fptr != NULL) { 4555088Sab196087 if (pclose(state.pager.fptr) == -1) 4565088Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_PAGERFINI)); 4575088Sab196087 4585088Sab196087 state.pager.fptr = NULL; 4595088Sab196087 } 4605088Sab196087 } 4615088Sab196087 4625088Sab196087 4635088Sab196087 /* 4645088Sab196087 * Print general formtted text for the user, using printf()-style 4655088Sab196087 * formatting. Uses the pager process if one has been started, or 4665088Sab196087 * stdout otherwise. 4675088Sab196087 */ 4685088Sab196087 void 4695088Sab196087 elfedit_printf(const char *format, ...) 4705088Sab196087 { 4715088Sab196087 va_list args; 4725088Sab196087 int err; 4735088Sab196087 FILE *fptr; 4745088Sab196087 int pager; 4755088Sab196087 int broken_pipe = 0; 4765088Sab196087 4775088Sab196087 /* 4785088Sab196087 * If there is a pager process, then use it. Otherwise write 4795088Sab196087 * directly to stdout. 4805088Sab196087 */ 4815088Sab196087 pager = (state.pager.fptr != NULL); 4825088Sab196087 fptr = pager ? state.pager.fptr : stdout; 4835088Sab196087 4845088Sab196087 va_start(args, format); 4855088Sab196087 errno = 0; 4865088Sab196087 err = vfprintf(fptr, format, args); 4875088Sab196087 4885088Sab196087 /* Did we fail because a child pager process has exited? */ 4895088Sab196087 broken_pipe = pager && (err < 0) && (errno == EPIPE); 4905088Sab196087 4915088Sab196087 va_end(args); 4925088Sab196087 4935088Sab196087 /* 4945088Sab196087 * On error, we simply issue the error without cleaning up 4955088Sab196087 * the pager process. The message code handles that as a standard 4965088Sab196087 * part of error processing. 4975088Sab196087 * 4985088Sab196087 * We handle failure due to an exited pager process differently 4995088Sab196087 * than a normal error, because it is usually due to the user 5005088Sab196087 * intentionally telling it to. 5015088Sab196087 */ 5025088Sab196087 if (err < 0) { 5035088Sab196087 if (broken_pipe) 5045088Sab196087 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL)); 5055088Sab196087 else 5065088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF)); 5075088Sab196087 } 5085088Sab196087 } 5095088Sab196087 5105088Sab196087 5115088Sab196087 /* 5125088Sab196087 * Some our modules use liblddb routines to format ELF output. 5135088Sab196087 * In order to ensure that such output is sent to the pager pipe 5145088Sab196087 * when there is one, and stdout otherwise, we redefine the dbg_print() 5155088Sab196087 * function here. 5165088Sab196087 * 5175088Sab196087 * This item should be defined NODIRECT. 5185088Sab196087 */ 5195088Sab196087 /* PRINTFLIKE2 */ 5205088Sab196087 void 5215088Sab196087 dbg_print(Lm_list *lml, const char *format, ...) 5225088Sab196087 { 5235088Sab196087 va_list ap; 5245088Sab196087 int err; 5255088Sab196087 FILE *fptr; 5265088Sab196087 int pager; 5275088Sab196087 int broken_pipe = 0; 5285088Sab196087 5295088Sab196087 #if defined(lint) 5305088Sab196087 /* 5315088Sab196087 * The lml argument is only meaningful for diagnostics sent to ld.so.1. 5325088Sab196087 * Supress the lint error by making a dummy assignment. 5335088Sab196087 */ 5345088Sab196087 lml = 0; 5355088Sab196087 #endif 5365088Sab196087 5375088Sab196087 /* 5385088Sab196087 * If there is a pager process, then use it. Otherwise write 5395088Sab196087 * directly to stdout. 5405088Sab196087 */ 5415088Sab196087 pager = (state.pager.fptr != NULL); 5425088Sab196087 fptr = pager ? state.pager.fptr : stdout; 5435088Sab196087 5445088Sab196087 va_start(ap, format); 5455088Sab196087 errno = 0; 5465088Sab196087 err = vfprintf(fptr, format, ap); 5475088Sab196087 if (err >= 0) 5485088Sab196087 err = fprintf(fptr, MSG_ORIG(MSG_STR_NL)); 5495088Sab196087 5505088Sab196087 /* Did we fail because a child pager process has exited? */ 5515088Sab196087 broken_pipe = (err < 0) && pager && (errno == EPIPE); 5525088Sab196087 5535088Sab196087 va_end(ap); 5545088Sab196087 5555088Sab196087 /* 5565088Sab196087 * On error, we simply issue the error without cleaning up 5575088Sab196087 * the pager process. The message code handles that as a standard 5585088Sab196087 * part of error processing. 5595088Sab196087 * 5605088Sab196087 * We handle failure due to an exited pager process differently 5615088Sab196087 * than a normal error, because it is usually due to the user 5625088Sab196087 * intentionally telling it to. 5635088Sab196087 */ 5645088Sab196087 if (err < 0) { 5655088Sab196087 if (broken_pipe) 5665088Sab196087 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL)); 5675088Sab196087 else 5685088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF)); 5695088Sab196087 } 5705088Sab196087 } 5715088Sab196087 5725088Sab196087 5735088Sab196087 /* 5745088Sab196087 * Write raw bytes of text in a manner similar to fwrite(). 5755088Sab196087 * Uses the pager process if one has been started, or 5765088Sab196087 * stdout otherwise. 5775088Sab196087 */ 5785088Sab196087 void 5795088Sab196087 elfedit_write(const void *ptr, size_t size) 5805088Sab196087 { 5815088Sab196087 FILE *fptr; 5825088Sab196087 int err; 5835088Sab196087 5845088Sab196087 /* 5855088Sab196087 * If there is a pager process, then use it. Otherwise write 5865088Sab196087 * directly to stdout. 5875088Sab196087 */ 5885088Sab196087 fptr = (state.pager.fptr == NULL) ? stdout : state.pager.fptr; 5895088Sab196087 5905088Sab196087 if (fwrite(ptr, 1, size, fptr) != size) { 5915088Sab196087 err = errno; 5925088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FWRITE), 5935088Sab196087 strerror(err)); 5945088Sab196087 } 5955088Sab196087 } 5965088Sab196087 5975088Sab196087 5985088Sab196087 /* 5995892Sab196087 * Convert the NULL terminated string to the form used by the C 600*6635Sab196087 * language to represent literal strings. See conv_str_to_c_literal() 601*6635Sab196087 * for details. 602*6635Sab196087 * 603*6635Sab196087 * This routine differs from conv_str_to_c_literal() in two ways: 604*6635Sab196087 * 1) String is NULL terminated instead of counted 605*6635Sab196087 * 2) Signature of outfunc 6065892Sab196087 * 6075892Sab196087 * entry: 6085892Sab196087 * str - String to be processed 6095892Sab196087 * outfunc - Function to be called to move output characters. Note 6105892Sab196087 * that this function has the same signature as elfedit_write(), 6115892Sab196087 * and that function can be used to write the characters to 6125892Sab196087 * the output. 6135892Sab196087 * 6145892Sab196087 * exit: 6155892Sab196087 * The string has been processed, with the resulting data passed 6165892Sab196087 * to outfunc for processing. 6175892Sab196087 */ 618*6635Sab196087 static void 619*6635Sab196087 elfedit_str_to_c_literal_cb(const void *ptr, size_t size, void *uvalue) 620*6635Sab196087 { 621*6635Sab196087 elfedit_write_func_t *outfunc = (elfedit_write_func_t *)uvalue; 622*6635Sab196087 623*6635Sab196087 (* outfunc)(ptr, size); 624*6635Sab196087 625*6635Sab196087 } 6265892Sab196087 void 6275892Sab196087 elfedit_str_to_c_literal(const char *str, elfedit_write_func_t *outfunc) 6285892Sab196087 { 629*6635Sab196087 conv_str_to_c_literal(str, strlen(str), 630*6635Sab196087 elfedit_str_to_c_literal_cb, (void *) outfunc); 6315892Sab196087 } 6325892Sab196087 6335892Sab196087 6345892Sab196087 /* 6355088Sab196087 * Wrappers on malloc() and realloc() that check the result for success 6365088Sab196087 * and issue an error if not. The caller can use the result of these 6375088Sab196087 * functions without checking for a NULL pointer, as we do not return to 6385088Sab196087 * the caller in the failure case. 6395088Sab196087 */ 6405088Sab196087 void * 6415088Sab196087 elfedit_malloc(const char *item_name, size_t size) 6425088Sab196087 { 6435088Sab196087 void *m; 6445088Sab196087 6455088Sab196087 m = malloc(size); 6465088Sab196087 if (m == NULL) { 6475088Sab196087 int err = errno; 6485088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC), 6495088Sab196087 item_name, strerror(err)); 6505088Sab196087 } 6515088Sab196087 6525088Sab196087 return (m); 6535088Sab196087 } 6545088Sab196087 6555088Sab196087 void * 6565088Sab196087 elfedit_realloc(const char *item_name, void *ptr, size_t size) 6575088Sab196087 { 6585088Sab196087 void *m; 6595088Sab196087 6605088Sab196087 m = realloc(ptr, size); 6615088Sab196087 if (m == NULL) { 6625088Sab196087 int err = errno; 6635088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC), 6645088Sab196087 item_name, strerror(err)); 6655088Sab196087 } 6665088Sab196087 6675088Sab196087 return (m); 6685088Sab196087 } 6695088Sab196087 6705088Sab196087 6715088Sab196087 /* 6725088Sab196087 * Ensure that the given buffer has room for n bytes of data. 6735088Sab196087 */ 6745088Sab196087 static void 6755088Sab196087 strbuf_ensure_size(STRBUF *str, size_t size) 6765088Sab196087 { 6775088Sab196087 #define INITIAL_STR_ALLOC 128 6785088Sab196087 6795088Sab196087 size_t n; 6805088Sab196087 6815088Sab196087 n = (str->n == 0) ? INITIAL_STR_ALLOC : str->n; 6825088Sab196087 while (size > n) /* Double buffer until string fits */ 6835088Sab196087 n *= 2; 6845088Sab196087 if (n != str->n) { /* Alloc new string buffer if needed */ 6855088Sab196087 str->buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_UCMDSTR), 6865088Sab196087 str->buf, n); 6875088Sab196087 str->n = n; 6885088Sab196087 } 6895088Sab196087 6905088Sab196087 #undef INITIAL_STR_ALLOC 6915088Sab196087 } 6925088Sab196087 6935088Sab196087 6945088Sab196087 /* 6955088Sab196087 * Extract the argument/option information for the next item referenced 6965088Sab196087 * by optarg, and advance the pointer to the next item. 6975088Sab196087 * 6985088Sab196087 * entry: 6995088Sab196087 * optarg - Address of pointer to argument or option array 7005088Sab196087 * item - Struct to be filled in. 7015088Sab196087 * 7025088Sab196087 * exit: 7035088Sab196087 * The item block has been filled in with the information for 7045088Sab196087 * the next item in the optarg array. *optarg has been advanced 7055088Sab196087 * to the next item. 7065088Sab196087 */ 7075088Sab196087 void 7085088Sab196087 elfedit_next_optarg(elfedit_cmd_optarg_t **optarg, elfedit_optarg_item_t *item) 7095088Sab196087 { 7105088Sab196087 /* 7115088Sab196087 * Array of inheritable options/arguments. Indexed by one less 7125088Sab196087 * than the corresponding ELFEDIT_STDOA_ value. 7135088Sab196087 */ 7145088Sab196087 static const elfedit_optarg_item_t stdoa[] = { 7155088Sab196087 /* ELFEDIT_STDOA_O */ 7165088Sab196087 { MSG_ORIG(MSG_STR_MINUS_O), MSG_ORIG(MSG_STR_OUTSTYLE), 7175088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_O) */ 7185088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_O, 7195088Sab196087 ELFEDIT_CMDOA_F_VALUE }, 7205088Sab196087 7215088Sab196087 /* ELFEDIT_STDOA_AND */ 7225088Sab196087 { MSG_ORIG(MSG_STR_MINUS_AND), NULL, 7235088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_AND) */ 7245088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_AND, 0 }, 7255088Sab196087 7265088Sab196087 /* ELFEDIT_STDOA_CMP */ 7275088Sab196087 { MSG_ORIG(MSG_STR_MINUS_CMP), NULL, 7285088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */ 7295088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_CMP, 0 }, 7305088Sab196087 7315088Sab196087 /* ELFEDIT_STDOA_OR */ 7325088Sab196087 { MSG_ORIG(MSG_STR_MINUS_OR), NULL, 7335088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_OR) */ 7345088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_OR, 0 }, 7355088Sab196087 }; 7365088Sab196087 7375088Sab196087 elfedit_cmd_optarg_t *oa; 7385088Sab196087 7395088Sab196087 7405088Sab196087 /* Grab first item, advance the callers pointer over it */ 7415088Sab196087 oa = (*optarg)++; 7425088Sab196087 7435088Sab196087 if (oa->oa_flags & ELFEDIT_CMDOA_F_INHERIT) { 7445088Sab196087 /* Values are pre-chewed in the stdoa array above */ 7455088Sab196087 *item = stdoa[((uintptr_t)oa->oa_name) - 1]; 7465088Sab196087 7475088Sab196087 /* 7485088Sab196087 * Set the inherited flag so that elfedit_optarg_helpstr() 7495088Sab196087 * can tell who is responsible for translating the help string. 7505088Sab196087 */ 7515088Sab196087 item->oai_flags |= ELFEDIT_CMDOA_F_INHERIT; 7525088Sab196087 } else { /* Non-inherited item */ 7535088Sab196087 item->oai_name = oa->oa_name; 7545088Sab196087 if ((oa->oa_flags & ELFEDIT_CMDOA_F_VALUE) != 0) { 7555088Sab196087 item->oai_vname = oa[1].oa_name; 7565088Sab196087 7575088Sab196087 /* Advance users pointer past value element */ 7585088Sab196087 (*optarg)++; 7595088Sab196087 } else { 7605088Sab196087 item->oai_vname = NULL; 7615088Sab196087 } 7625088Sab196087 item->oai_help = oa->oa_help; 7635088Sab196087 item->oai_flags = oa->oa_flags; 7645088Sab196087 } 7655088Sab196087 7665088Sab196087 /* 7675088Sab196087 * The module determines the idmask and excmask fields whether 7685088Sab196087 * or not inheritance is in play. 7695088Sab196087 */ 7705088Sab196087 item->oai_idmask = oa->oa_idmask; 7715088Sab196087 item->oai_excmask = oa->oa_excmask; 7725088Sab196087 } 7735088Sab196087 7745088Sab196087 7755088Sab196087 7765088Sab196087 /* 7775088Sab196087 * Return the help string for an option/argument item, as returned 7785088Sab196087 * by elfedit_next_optarg(). This routine handles the details of 7795088Sab196087 * knowing whether the string is provided by elfedit itself (inherited), 7805088Sab196087 * or needs to be translated by the module. 7815088Sab196087 */ 7825088Sab196087 const char * 7835088Sab196087 elfedit_optarg_helpstr(elfeditGC_module_t *mod, elfedit_optarg_item_t *item) 7845088Sab196087 { 7855088Sab196087 /* 7865088Sab196087 * The help string from an inherited item comes right out 7875088Sab196087 * of the main elfedit string table. 7885088Sab196087 */ 7895088Sab196087 if (item->oai_flags & ELFEDIT_CMDOA_F_INHERIT) 7905088Sab196087 return (MSG_INTL((Msg) item->oai_help)); 7915088Sab196087 7925088Sab196087 /* 7935088Sab196087 * If the string is defined by the module, then we need to 7945088Sab196087 * have the module translate it for us. 7955088Sab196087 */ 7965088Sab196087 return ((* mod->mod_i18nhdl_to_str)(item->oai_help)); 7975088Sab196087 } 7985088Sab196087 7995088Sab196087 8005088Sab196087 8015088Sab196087 /* 8025088Sab196087 * Used by usage_optarg() to insert a character into the output buffer, 8035088Sab196087 * advancing the buffer pointer and current column, and reducing the 8045088Sab196087 * amount of remaining space. 8055088Sab196087 */ 8065088Sab196087 static void 8075088Sab196087 usage_optarg_insert_ch(int ch, char **cur, size_t *n, size_t *cur_col) 8085088Sab196087 { 8095088Sab196087 8105088Sab196087 *(*cur)++ = ch; 8115088Sab196087 **cur = '\0'; 8125088Sab196087 (*n)--; 8135088Sab196087 (*cur_col)++; 8145088Sab196087 } 8155088Sab196087 8165088Sab196087 /* 8175088Sab196087 * Used by usage_optarg() to insert a string into the output 8185088Sab196087 * buffer, advancing the buffer pointer and current column, and reducing 8195088Sab196087 * the amount of remaining space. 8205088Sab196087 */ 8215088Sab196087 static void 8225088Sab196087 usage_optarg_insert_str(char **cur, size_t *n, size_t *cur_col, 8235088Sab196087 const char *format, ...) 8245088Sab196087 { 8255088Sab196087 size_t len; 8265088Sab196087 va_list args; 8275088Sab196087 8285088Sab196087 va_start(args, format); 8295088Sab196087 len = vsnprintf(*cur, *n, format, args); 8305088Sab196087 va_end(args); 8315088Sab196087 8325088Sab196087 *cur += len; 8335088Sab196087 *n -= len; 8345088Sab196087 *cur_col += len; 8355088Sab196087 } 8365088Sab196087 /* 8375088Sab196087 * Used by usage_optarg() to insert an optarg item string into the output 8385088Sab196087 * buffer, advancing the buffer pointer and current column, and reducing 8395088Sab196087 * the amount of remaining space. 8405088Sab196087 */ 8415088Sab196087 static void 8425088Sab196087 usage_optarg_insert_item(elfedit_optarg_item_t *item, char **cur, 8435088Sab196087 size_t *n, size_t *cur_col) 8445088Sab196087 { 8455088Sab196087 size_t len; 8465088Sab196087 8475088Sab196087 if (item->oai_flags & ELFEDIT_CMDOA_F_VALUE) { 8485088Sab196087 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG2), 8495088Sab196087 item->oai_name, item->oai_vname); 8505088Sab196087 } else { 8515088Sab196087 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG), 8525088Sab196087 item->oai_name); 8535088Sab196087 } 8545088Sab196087 *cur += len; 8555088Sab196087 *n -= len; 8565088Sab196087 *cur_col += len; 8575088Sab196087 } 8585088Sab196087 8595088Sab196087 8605088Sab196087 8615088Sab196087 /* 8625088Sab196087 * Write the options/arguments to the usage string. 8635088Sab196087 * 8645088Sab196087 * entry: 8655088Sab196087 * main_buf_n - Size of main buffer from which buf and buf_n are 8665088Sab196087 * allocated. 8675088Sab196087 * buf - Address of pointer to where next item is to be placed. 8685088Sab196087 * buf_n - Address of count of remaining bytes in buffer 8695088Sab196087 * buf_cur_col - Address of current output column for current line 8705088Sab196087 * of generated string. 8715088Sab196087 * optarg - Options list 8725088Sab196087 * isopt - True if these are options, false for arguments. 8735088Sab196087 * wrap_str - String to indent wrapped lines. If NULL, lines 8745088Sab196087 * are not wrapped 8755088Sab196087 */ 8765088Sab196087 static void 8775088Sab196087 usage_optarg(size_t main_buf_n, char **buf, size_t *buf_n, size_t *buf_cur_col, 8785088Sab196087 elfedit_cmd_optarg_t *optarg, int isopt, const char *wrap_str) 8795088Sab196087 { 8805088Sab196087 /* 8815088Sab196087 * An option can be combined into a simple format if it lacks 8825088Sab196087 * these flags and is only one character in length. 8835088Sab196087 */ 8845088Sab196087 static const elfedit_cmd_oa_flag_t exflags = 8855088Sab196087 (ELFEDIT_CMDOA_F_VALUE | ELFEDIT_CMDOA_F_MULT); 8865088Sab196087 8875088Sab196087 /* 8885088Sab196087 * A static buffer, which is grown as needed to accomodate 8895088Sab196087 * the maximum usage string seen. 8905088Sab196087 */ 8915088Sab196087 static STRBUF simple_str; 8925088Sab196087 8935088Sab196087 char *cur = *buf; 8945088Sab196087 size_t n = *buf_n; 8955088Sab196087 size_t cur_col = *buf_cur_col; 8965088Sab196087 int len; 8975088Sab196087 int use_simple = 0; 8985088Sab196087 elfedit_optarg_item_t item; 8995088Sab196087 elfedit_cmd_oa_mask_t optmask = 0; 9005088Sab196087 int use_bkt; 9015088Sab196087 9025088Sab196087 /* 9035088Sab196087 * If processing options, pull the 1-character ones that don't have 9045088Sab196087 * an associated value and don't have any mutual exclusion issues into 9055088Sab196087 * a single combination string to go at the beginning of the usage. 9065088Sab196087 */ 9075088Sab196087 if (isopt) { 9085088Sab196087 elfedit_cmd_optarg_t *tmp_optarg = optarg; 9095088Sab196087 char *s; 9105088Sab196087 9115088Sab196087 /* 9125088Sab196087 * The simple string is guaranteed to fit in the same 9135088Sab196087 * amount of space reserved for the main buffer. 9145088Sab196087 */ 9155088Sab196087 strbuf_ensure_size(&simple_str, main_buf_n); 9165088Sab196087 s = simple_str.buf; 9175088Sab196087 *s++ = ' '; 9185088Sab196087 *s++ = '['; 9195088Sab196087 *s++ = '-'; 9205088Sab196087 while (tmp_optarg->oa_name != NULL) { 9215088Sab196087 elfedit_next_optarg(&tmp_optarg, &item); 9225088Sab196087 if (((item.oai_flags & exflags) == 0) && 9235088Sab196087 (item.oai_name[2] == '\0') && 9245088Sab196087 (item.oai_excmask == 0)) { 9255088Sab196087 optmask |= item.oai_idmask; 9265088Sab196087 *s++ = item.oai_name[1]; 9275088Sab196087 } 9285088Sab196087 } 9295088Sab196087 9305088Sab196087 /* 9315088Sab196087 * If we found more than one, then finish the string and 9325088Sab196087 * add it. Don't do this for a single option, because 9335088Sab196087 * it looks better in that case if the option shows up 9345088Sab196087 * in alphabetical order rather than being hoisted. 9355088Sab196087 */ 9365088Sab196087 use_simple = (s > (simple_str.buf + 4)); 9375088Sab196087 if (use_simple) { 9385088Sab196087 *s++ = ']'; 9395088Sab196087 *s++ = '\0'; 9405088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col, 9415088Sab196087 MSG_ORIG(MSG_STR_HLPOPTARG), simple_str.buf); 9425088Sab196087 } else { 9435088Sab196087 /* Not using it, so reset the cumulative options mask */ 9445088Sab196087 optmask = 0; 9455088Sab196087 } 9465088Sab196087 } 9475088Sab196087 9485088Sab196087 while (optarg->oa_name != NULL) { 9495088Sab196087 elfedit_next_optarg(&optarg, &item); 9505088Sab196087 9515088Sab196087 if (isopt) { 9525088Sab196087 /* 9535088Sab196087 * If this is an option that was pulled into the 9545088Sab196087 * combination string above, then skip over it. 9555088Sab196087 */ 9565088Sab196087 if (use_simple && ((item.oai_flags & exflags) == 0) && 9575088Sab196087 (item.oai_name[2] == '\0') && 9585088Sab196087 (item.oai_excmask == 0)) 9595088Sab196087 continue; 9605088Sab196087 9615088Sab196087 /* 9625088Sab196087 * If this is a mutual exclusion option that was 9635088Sab196087 * picked up out of order by a previous iteration 9645088Sab196087 * of this loop, then skip over it. 9655088Sab196087 */ 9665088Sab196087 if ((optmask & item.oai_idmask) != 0) 9675088Sab196087 continue; 9685088Sab196087 9695088Sab196087 /* Add this item to the accumulating options mask */ 9705088Sab196087 optmask |= item.oai_idmask; 9715088Sab196087 } 9725088Sab196087 9735088Sab196087 /* Wrap line, or insert blank separator */ 9745088Sab196087 if ((wrap_str != NULL) && (cur_col > USAGE_WRAP_COL)) { 9755088Sab196087 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_WRAPUSAGE), 9765088Sab196087 wrap_str); 9775088Sab196087 cur += len; 9785088Sab196087 n -= len; 9795088Sab196087 cur_col = len - 1; /* Don't count the newline */ 9805088Sab196087 } else { 9815088Sab196087 usage_optarg_insert_ch(' ', &cur, &n, &cur_col); 9825088Sab196087 } 9835088Sab196087 9845088Sab196087 use_bkt = (item.oai_flags & ELFEDIT_CMDOA_F_OPT) || isopt; 9855088Sab196087 if (use_bkt) 9865088Sab196087 usage_optarg_insert_ch('[', &cur, &n, &cur_col); 9875088Sab196087 9885088Sab196087 /* Add the item to the buffer */ 9895088Sab196087 usage_optarg_insert_item(&item, &cur, &n, &cur_col); 9905088Sab196087 9915088Sab196087 /* 9925088Sab196087 * If this item has a non-zero mutual exclusion mask, 9935088Sab196087 * then look for the other items and display them all 9945088Sab196087 * together with alternation (|). Note that plain arguments 9955088Sab196087 * cannot have a non-0 exclusion mask, so this is 9965088Sab196087 * effectively options-only (isopt != 0). 9975088Sab196087 */ 9985088Sab196087 if (item.oai_excmask != 0) { 9995088Sab196087 elfedit_cmd_optarg_t *tmp_optarg = optarg; 10005088Sab196087 elfedit_optarg_item_t tmp_item; 10015088Sab196087 10025088Sab196087 /* 10035088Sab196087 * When showing alternation, elipses for multiple 10045088Sab196087 * copies need to appear inside the [] brackets. 10055088Sab196087 */ 10065088Sab196087 if (item.oai_flags & ELFEDIT_CMDOA_F_MULT) 10075088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col, 10085088Sab196087 MSG_ORIG(MSG_STR_ELIPSES)); 10095088Sab196087 10105088Sab196087 10115088Sab196087 while (tmp_optarg->oa_name != NULL) { 10125088Sab196087 elfedit_next_optarg(&tmp_optarg, &tmp_item); 10135088Sab196087 if ((item.oai_excmask & tmp_item.oai_idmask) == 10145088Sab196087 0) 10155088Sab196087 continue; 10165088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col, 10175088Sab196087 MSG_ORIG(MSG_STR_SP_BAR_SP)); 10185088Sab196087 usage_optarg_insert_item(&tmp_item, 10195088Sab196087 &cur, &n, &cur_col); 10205088Sab196087 10215088Sab196087 /* 10225088Sab196087 * Add it to the mask of seen options. 10235088Sab196087 * This will keep us from showing it twice. 10245088Sab196087 */ 10255088Sab196087 optmask |= tmp_item.oai_idmask; 10265088Sab196087 } 10275088Sab196087 } 10285088Sab196087 if (use_bkt) 10295088Sab196087 usage_optarg_insert_ch(']', &cur, &n, &cur_col); 10305088Sab196087 10315088Sab196087 /* 10325088Sab196087 * If alternation was not shown above (non-zero exclusion mask) 10335088Sab196087 * then the elipses for multiple copies are shown outside 10345088Sab196087 * any [] brackets. 10355088Sab196087 */ 10365088Sab196087 if ((item.oai_excmask == 0) && 10375088Sab196087 (item.oai_flags & ELFEDIT_CMDOA_F_MULT)) 10385088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col, 10395088Sab196087 MSG_ORIG(MSG_STR_ELIPSES)); 10405088Sab196087 10415088Sab196087 } 10425088Sab196087 10435088Sab196087 *buf = cur; 10445088Sab196087 *buf_n = n; 10455088Sab196087 *buf_cur_col = cur_col; 10465088Sab196087 } 10475088Sab196087 10485088Sab196087 10495088Sab196087 10505088Sab196087 /* 10515088Sab196087 * Format the usage string for a command into a static buffer and 10525088Sab196087 * return the pointer to the user. The resultant string is valid 10535088Sab196087 * until the next call to this routine, and which point it 10545088Sab196087 * will be overwritten or the memory is freed. 10555088Sab196087 * 10565088Sab196087 * entry: 10575088Sab196087 * mod, cmd - Module and command definitions for command to be described 10585088Sab196087 * wrap_str - NULL, or string to be used to indent when 10595088Sab196087 * lines are wrapped. If NULL, no wrapping is done, and 10605088Sab196087 * all output is on a single line. 10615088Sab196087 * cur_col - Starting column at which the string will be displayed. 10625088Sab196087 * Ignored if wrap_str is NULL. 10635088Sab196087 */ 10645088Sab196087 const char * 10655088Sab196087 elfedit_format_command_usage(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd, 10665088Sab196087 const char *wrap_str, size_t cur_col) 10675088Sab196087 { 10685088Sab196087 10695088Sab196087 /* 10705088Sab196087 * A static buffer, which is grown as needed to accomodate 10715088Sab196087 * the maximum usage string seen. 10725088Sab196087 */ 10735088Sab196087 static STRBUF str; 10745088Sab196087 10755088Sab196087 elfedit_cmd_optarg_t *optarg; 10765088Sab196087 size_t len, n, elipses_len; 10775088Sab196087 char *cur; 10785088Sab196087 elfedit_optarg_item_t item; 10795088Sab196087 10805088Sab196087 /* 10815088Sab196087 * Estimate a worst case size for the usage string: 10825088Sab196087 * - module name 10835088Sab196087 * - lengths of the strings 10845088Sab196087 * - every option or argument is enclosed in brackets 10855088Sab196087 * - space in between each item, with an alternation (" | ") 10865088Sab196087 * - elipses will be displayed with each option and argument 10875088Sab196087 */ 10885088Sab196087 n = strlen(mod->mod_name) + strlen(cmd->cmd_name[0]) + 6; 10895088Sab196087 elipses_len = strlen(MSG_ORIG(MSG_STR_ELIPSES)); 10905088Sab196087 if ((optarg = cmd->cmd_opt) != NULL) 10915088Sab196087 while (optarg->oa_name != NULL) { 10925088Sab196087 elfedit_next_optarg(&optarg, &item); 10935088Sab196087 n += strlen(item.oai_name) + 5 + elipses_len; 10945088Sab196087 } 10955088Sab196087 if ((optarg = cmd->cmd_args) != NULL) 10965088Sab196087 while (optarg->oa_name != NULL) { 10975088Sab196087 elfedit_next_optarg(&optarg, &item); 10985088Sab196087 n += strlen(item.oai_name) + 5 + elipses_len; 10995088Sab196087 } 11005088Sab196087 n++; /* Null termination */ 11015088Sab196087 11025088Sab196087 /* 11035088Sab196087 * If wrapping lines, we insert a newline and then wrap_str 11045088Sab196087 * every USAGE_WRAP_COL characters. 11055088Sab196087 */ 11065088Sab196087 if (wrap_str != NULL) 11075088Sab196087 n += ((n + USAGE_WRAP_COL) / USAGE_WRAP_COL) * 11085088Sab196087 (strlen(wrap_str) + 1); 11095088Sab196087 11105088Sab196087 strbuf_ensure_size(&str, n); 11115088Sab196087 11125088Sab196087 /* Command name */ 11135088Sab196087 cur = str.buf; 11145088Sab196087 n = str.n; 11155088Sab196087 if (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) == 0) 11165088Sab196087 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_SYSCMD), 11175088Sab196087 cmd->cmd_name[0]); 11185088Sab196087 else 11195088Sab196087 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_MODCMD), 11205088Sab196087 mod->mod_name, cmd->cmd_name[0]); 11215088Sab196087 cur += len; 11225088Sab196087 n -= len; 11235088Sab196087 cur_col += len; 11245088Sab196087 11255088Sab196087 if (cmd->cmd_opt != NULL) 11265088Sab196087 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_opt, 11275088Sab196087 1, wrap_str); 11285088Sab196087 if (cmd->cmd_args != NULL) 11295088Sab196087 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_args, 11305088Sab196087 0, wrap_str); 11315088Sab196087 11325088Sab196087 return (str.buf); 11335088Sab196087 } 11345088Sab196087 11355088Sab196087 /* 11365088Sab196087 * Wrapper on elfedit_msg() that issues an ELFEDIT_MSG_USAGE 11375088Sab196087 * error giving usage information for the command currently 11385088Sab196087 * referenced by state.cur_cmd. 11395088Sab196087 */ 11405088Sab196087 void 11415088Sab196087 elfedit_command_usage(void) 11425088Sab196087 { 11435088Sab196087 elfedit_msg(ELFEDIT_MSG_CMDUSAGE, MSG_INTL(MSG_USAGE_CMD), 11445088Sab196087 elfedit_format_command_usage(state.cur_cmd->ucmd_mod, 11455088Sab196087 state.cur_cmd->ucmd_cmd, NULL, 0)); 11465088Sab196087 } 11475088Sab196087 11485088Sab196087 11495088Sab196087 /* 11505088Sab196087 * This function allows the loadable modules to get the command line 11515088Sab196087 * flags. 11525088Sab196087 */ 11535088Sab196087 elfedit_flag_t 11545088Sab196087 elfedit_flags(void) 11555088Sab196087 { 11565088Sab196087 return (state.flags); 11575088Sab196087 } 11585088Sab196087 11595088Sab196087 /* 11605088Sab196087 * This function is used to register a per-command invocation output style 11615088Sab196087 * that will momentarily override the global output style for the duration 11625088Sab196087 * of the current command. This function must only be called by an 11635088Sab196087 * active command. 11645088Sab196087 * 11655088Sab196087 * entry: 11665088Sab196087 * str - One of the valid strings for the output style 11675088Sab196087 */ 11685088Sab196087 void 11695088Sab196087 elfedit_set_cmd_outstyle(const char *str) 11705088Sab196087 { 11715088Sab196087 if ((state.cur_cmd != NULL) && (str != NULL)) { 11725088Sab196087 if (elfedit_atooutstyle(str, &state.cur_cmd->ucmd_ostyle) == 0) 11735088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 11745088Sab196087 MSG_INTL(MSG_ERR_BADOSTYLE), str); 11755088Sab196087 state.cur_cmd->ucmd_ostyle_set = 1; 11765088Sab196087 } 11775088Sab196087 } 11785088Sab196087 11795088Sab196087 /* 11805088Sab196087 * This function allows the loadable modules to get the output style. 11815088Sab196087 */ 11825088Sab196087 elfedit_outstyle_t 11835088Sab196087 elfedit_outstyle(void) 11845088Sab196087 { 11855088Sab196087 /* 11865088Sab196087 * If there is an active per-command output style, 11875088Sab196087 * return it. 11885088Sab196087 */ 11895088Sab196087 if ((state.cur_cmd != NULL) && (state.cur_cmd->ucmd_ostyle_set)) 11905088Sab196087 return (state.cur_cmd->ucmd_ostyle); 11915088Sab196087 11925088Sab196087 11935088Sab196087 return (state.outstyle); 11945088Sab196087 } 11955088Sab196087 11965088Sab196087 /* 11975088Sab196087 * Return the command descriptor of the currently executing command. 11985088Sab196087 * For use only by the modules or code called by the modules. 11995088Sab196087 */ 12005088Sab196087 elfeditGC_cmd_t * 12015088Sab196087 elfedit_curcmd(void) 12025088Sab196087 { 12035088Sab196087 return (state.cur_cmd->ucmd_cmd); 12045088Sab196087 } 12055088Sab196087 12065088Sab196087 /* 12075088Sab196087 * Build a dynamically allocated elfedit_obj_state_t struct that 12085088Sab196087 * contains a cache of the ELF file contents. This pre-chewed form 12095088Sab196087 * is fed to each command, reducing the amount of ELF boilerplate 12105088Sab196087 * code each command needs to contain. 12115088Sab196087 * 12125088Sab196087 * entry: 12135088Sab196087 * file - Name of file to process 12145088Sab196087 * 12155088Sab196087 * exit: 12165088Sab196087 * Fills state.elf with the necessary information for the open file. 12175088Sab196087 * 12185088Sab196087 * note: The resulting elfedit_obj_state_t is allocated from a single 12195088Sab196087 * piece of memory, such that a single call to free() suffices 12205088Sab196087 * to release it as well as any memory it references. 12215088Sab196087 */ 12225088Sab196087 static void 12235088Sab196087 init_obj_state(const char *file) 12245088Sab196087 { 12255088Sab196087 int fd; 12265088Sab196087 Elf *elf; 12275088Sab196087 int open_flag; 12285088Sab196087 12295088Sab196087 /* 12305088Sab196087 * In readonly mode, we open the file readonly so that it is 12315088Sab196087 * impossible to modify the file by accident. This also allows 12325088Sab196087 * us to access readonly files, perhaps in a case where we don't 12335088Sab196087 * intend to change it. 12345088Sab196087 * 12355088Sab196087 * We always use ELF_C_RDWR with elf_begin(), even in a readonly 12365088Sab196087 * session. This allows us to modify the in-memory image, which 12375088Sab196087 * can be useful when examining a file, even though we don't intend 12385088Sab196087 * to modify the on-disk data. The file is not writable in 12395088Sab196087 * this case, and we don't call elf_update(), so it is safe to do so. 12405088Sab196087 */ 12415088Sab196087 open_flag = ((state.flags & ELFEDIT_F_READONLY) ? O_RDONLY : O_RDWR); 12425088Sab196087 if ((fd = open(file, open_flag)) == -1) { 12435088Sab196087 int err = errno; 12445088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNFILE), 12455088Sab196087 file, strerror(err)); 12465088Sab196087 } 12475088Sab196087 (void) elf_version(EV_CURRENT); 12485088Sab196087 elf = elf_begin(fd, ELF_C_RDWR, NULL); 12495088Sab196087 if (elf == NULL) { 12505088Sab196087 (void) close(fd); 12515088Sab196087 elfedit_elferr(file, MSG_ORIG(MSG_ELF_BEGIN)); 12525088Sab196087 /*NOTREACHED*/ 12535088Sab196087 } 12545088Sab196087 12555088Sab196087 /* We only handle standalone ELF files */ 12565088Sab196087 switch (elf_kind(elf)) { 12575088Sab196087 case ELF_K_AR: 12585088Sab196087 (void) close(fd); 12595088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOAR), file); 12605088Sab196087 break; 12615088Sab196087 case ELF_K_ELF: 12625088Sab196087 break; 12635088Sab196087 default: 12645088Sab196087 (void) close(fd); 12655088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNRECELFFILE), 12665088Sab196087 file); 12675088Sab196087 break; 12685088Sab196087 } 12695088Sab196087 12705088Sab196087 /* 12715088Sab196087 * Tell libelf that we take responsibility for object layout. 12725088Sab196087 * Otherwise, it will compute "proper" values for layout and 12735088Sab196087 * alignment fields, and these values can overwrite the values 12745088Sab196087 * set in the elfedit session. We are modifying existing 12755088Sab196087 * objects --- the layout concerns have already been dealt 12765088Sab196087 * with when the object was built. 12775088Sab196087 */ 12785088Sab196087 (void) elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT); 12795088Sab196087 12805088Sab196087 /* Fill in state.elf.obj_state */ 12815088Sab196087 state.elf.elfclass = gelf_getclass(elf); 12825088Sab196087 switch (state.elf.elfclass) { 12835088Sab196087 case ELFCLASS32: 12845088Sab196087 elfedit32_init_obj_state(file, fd, elf); 12855088Sab196087 break; 12865088Sab196087 case ELFCLASS64: 12875088Sab196087 elfedit64_init_obj_state(file, fd, elf); 12885088Sab196087 break; 12895088Sab196087 default: 12905088Sab196087 (void) close(fd); 12915088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADELFCLASS), 12925088Sab196087 file); 12935088Sab196087 break; 12945088Sab196087 } 12955088Sab196087 } 12965088Sab196087 12975088Sab196087 12985892Sab196087 #ifdef DEBUG_MODULE_LIST 12995088Sab196087 /* 13005088Sab196087 * Debug routine. Dump the module list to stdout. 13015088Sab196087 */ 13025088Sab196087 static void 13035088Sab196087 dbg_module_list(char *title) 13045088Sab196087 { 13055088Sab196087 MODLIST_T *m; 13065088Sab196087 13075088Sab196087 printf("<MODULE LIST: %s>\n", title); 13085088Sab196087 for (m = state.modlist; m != NULL; m = m->next) { 13095088Sab196087 printf("Module: >%s<\n", m->mod->mod_name); 13105088Sab196087 printf(" hdl: %llx\n", m->dl_hdl); 13115088Sab196087 printf(" path: >%s<\n", m->path ? m->path : "<builtin>"); 13125088Sab196087 } 13135088Sab196087 printf("<END OF MODULE LIST>\n"); 13145088Sab196087 } 13155088Sab196087 #endif 13165088Sab196087 13175088Sab196087 13185088Sab196087 /* 13195088Sab196087 * Search the module list for the named module. 13205088Sab196087 * 13215088Sab196087 * entry: 13225088Sab196087 * name - Name of module to find 13235088Sab196087 * insdef - Address of variable to receive address of predecessor 13245088Sab196087 * node to the desired one. 13255088Sab196087 * 13265088Sab196087 * exit: 13275088Sab196087 * If the module is it is found, this routine returns the pointer to 13285088Sab196087 * its MODLIST_T structure. *insdef references the predecessor node, or 13295088Sab196087 * is NULL if the found item is at the head of the list. 13305088Sab196087 * 13315088Sab196087 * If the module is not found, NULL is returned. *insdef references 13325088Sab196087 * the predecessor node of the position where an entry for this module 13335088Sab196087 * would be placed, or NULL if it would go at the beginning. 13345088Sab196087 */ 13355088Sab196087 static MODLIST_T * 13365088Sab196087 module_loaded(const char *name, MODLIST_T **insdef) 13375088Sab196087 { 13385088Sab196087 MODLIST_T *moddef; 13395088Sab196087 int cmp; 13405088Sab196087 13415088Sab196087 *insdef = NULL; 13425088Sab196087 moddef = state.modlist; 13435088Sab196087 if (moddef != NULL) { 13445088Sab196087 cmp = strcasecmp(name, moddef->ml_mod->mod_name); 13455088Sab196087 if (cmp == 0) { /* Desired module is first in list */ 13465088Sab196087 return (moddef); 13475088Sab196087 } else if (cmp > 0) { /* cmp > 0: Insert in middle/end */ 13485088Sab196087 *insdef = moddef; 13495088Sab196087 moddef = moddef->ml_next; 13505088Sab196087 cmp = -1; 13515088Sab196087 while (moddef && (cmp < 0)) { 13525088Sab196087 cmp = strcasecmp(moddef->ml_mod->mod_name, 13535088Sab196087 name); 13545088Sab196087 if (cmp == 0) 13555088Sab196087 return (moddef); 13565088Sab196087 if (cmp < 0) { 13575088Sab196087 *insdef = moddef; 13585088Sab196087 moddef = (*insdef)->ml_next; 13595088Sab196087 } 13605088Sab196087 } 13615088Sab196087 } 13625088Sab196087 } 13635088Sab196087 13645088Sab196087 return (NULL); 13655088Sab196087 } 13665088Sab196087 13675088Sab196087 13685088Sab196087 /* 13695088Sab196087 * Determine if a file is a sharable object based on its file path. 13705088Sab196087 * If path ends in a .so, followed optionally by a period and 1 or more 13715088Sab196087 * digits, we say that it is and return a pointer to the first character 13725088Sab196087 * of the suffix. Otherwise NULL is returned. 13735088Sab196087 */ 13745088Sab196087 static const char * 13755088Sab196087 path_is_so(const char *path) 13765088Sab196087 { 13775088Sab196087 int dotso_len; 13785088Sab196087 const char *tail; 13795088Sab196087 size_t len; 13805088Sab196087 13815088Sab196087 len = strlen(path); 13825088Sab196087 if (len == 0) 13835088Sab196087 return (NULL); 13845088Sab196087 tail = path + len; 13855088Sab196087 if (isdigit(*(tail - 1))) { 13865088Sab196087 while ((tail > path) && isdigit(*(tail - 1))) 13875088Sab196087 tail--; 13885088Sab196087 if ((tail <= path) || (*tail != '.')) 13895088Sab196087 return (NULL); 13905088Sab196087 } 13915088Sab196087 dotso_len = strlen(MSG_ORIG(MSG_STR_DOTSO)); 13925088Sab196087 if ((tail - path) < dotso_len) 13935088Sab196087 return (NULL); 13945088Sab196087 tail -= dotso_len; 13955088Sab196087 if (strncmp(tail, MSG_ORIG(MSG_STR_DOTSO), dotso_len) == 0) 13965088Sab196087 return (tail); 13975088Sab196087 13985088Sab196087 return (NULL); 13995088Sab196087 } 14005088Sab196087 14015088Sab196087 14025088Sab196087 /* 14035088Sab196087 * Locate the start of the unsuffixed file name within path. Returns pointer 14045088Sab196087 * to first character of that name in path. 14055088Sab196087 * 14065088Sab196087 * entry: 14075088Sab196087 * path - Path to be examined. 14085088Sab196087 * tail - NULL, or pointer to position at tail of path from which 14095088Sab196087 * the search for '/' characters should start. If NULL, 14105088Sab196087 * strlen() is used to locate the end of the string. 14115088Sab196087 * buf - NULL, or buffer to receive a copy of the characters that 14125088Sab196087 * lie between the start of the filename and tail. 14135088Sab196087 * bufsize - sizeof(buf) 14145088Sab196087 * 14155088Sab196087 * exit: 14165088Sab196087 * The pointer to the first character of the unsuffixed file name 14175088Sab196087 * within path is returned. If buf is non-NULL, the characters 14185088Sab196087 * lying between that point and tail (or the end of path if tail 14195088Sab196087 * is NULL) are copied into buf. 14205088Sab196087 */ 14215088Sab196087 static const char * 14225088Sab196087 elfedit_basename(const char *path, const char *tail, char *buf, size_t bufsiz) 14235088Sab196087 { 14245088Sab196087 const char *s; 14255088Sab196087 14265088Sab196087 if (tail == NULL) 14275088Sab196087 tail = path + strlen(path); 14285088Sab196087 s = tail; 14295088Sab196087 while ((s > path) && (*(s - 1) != '/')) 14305088Sab196087 s--; 14315088Sab196087 if (buf != NULL) 14325088Sab196087 elfedit_strnbcpy(buf, s, tail - s, bufsiz); 14335088Sab196087 return (s); 14345088Sab196087 } 14355088Sab196087 14365088Sab196087 14375088Sab196087 /* 14385088Sab196087 * Issue an error on behalf of load_module(), taking care to release 14395088Sab196087 * resources that routine may have aquired: 14405088Sab196087 * 14415088Sab196087 * entry: 14425088Sab196087 * moddef - NULL, or a module definition to be released via free() 14435088Sab196087 * dl_hdl - NULL, or a handle to a sharable object to release via 14445088Sab196087 * dlclose(). 14455088Sab196087 * dl_path - If dl_hdl is non-NULL, the path to the sharable object 14465088Sab196087 * file that was loaded. 14475088Sab196087 * format - A format string to pass to elfedit_msg(), containing 14485088Sab196087 * no more than (3) %s format codes, and no other format codes. 14495088Sab196087 * [s1-s4] - Strings to pass to elfedit_msg() to satisfy the four 14505088Sab196087 * allowed %s codes in format. Should be set to NULL if the 14515088Sab196087 * format string does not need them. 14525088Sab196087 * 14535088Sab196087 * note: 14545088Sab196087 * This routine makes a copy of the s1-s4 strings before freeing any 14555088Sab196087 * memory or unmapping the sharable library. It is therefore safe to 14565088Sab196087 * use strings from moddef, or from the sharable library (which will 14575088Sab196087 * be unmapped) to satisfy the other arguments s1-s4. 14585088Sab196087 */ 14595088Sab196087 static void 14605088Sab196087 load_module_err(MODLIST_T *moddef, void *dl_hdl, const char *dl_path, 14615088Sab196087 const char *format, const char *s1, const char *s2, const char *s3, 14625088Sab196087 const char *s4) 14635088Sab196087 { 14645088Sab196087 #define SCRBUFSIZE (PATH_MAX + 256) /* A path, plus some extra */ 14655088Sab196087 14665088Sab196087 char s1_buf[SCRBUFSIZE]; 14675088Sab196087 char s2_buf[SCRBUFSIZE]; 14685088Sab196087 char s3_buf[SCRBUFSIZE]; 14695088Sab196087 char s4_buf[SCRBUFSIZE]; 14705088Sab196087 14715088Sab196087 /* 14725088Sab196087 * The caller may provide strings for s1-s3 that are from 14735088Sab196087 * moddef. If we free moddef, the printf() will die on access 14745088Sab196087 * to free memory. We could push back on the user and force 14755088Sab196087 * each call to carefully make copies of such data. However, this 14765088Sab196087 * is an easy case to miss. Furthermore, this is an error case, 14775088Sab196087 * and machine efficiency is not the main issue. We therefore make 14785088Sab196087 * copies of the s1-s3 strings here into auto variables, and then 14795088Sab196087 * use those copies. The user is freed from worrying about it. 14805088Sab196087 * 14815088Sab196087 * We use oversized stack based buffers instead of malloc() to 14825088Sab196087 * reduce the number of ways that things can go wrong while 14835088Sab196087 * reporting the error. 14845088Sab196087 */ 14855088Sab196087 if (s1 != NULL) 14865088Sab196087 (void) strlcpy(s1_buf, s1, sizeof (s1_buf)); 14875088Sab196087 if (s2 != NULL) 14885088Sab196087 (void) strlcpy(s2_buf, s2, sizeof (s2_buf)); 14895088Sab196087 if (s3 != NULL) 14905088Sab196087 (void) strlcpy(s3_buf, s3, sizeof (s3_buf)); 14915088Sab196087 if (s4 != NULL) 14925088Sab196087 (void) strlcpy(s4_buf, s4, sizeof (s4_buf)); 14935088Sab196087 14945088Sab196087 14955088Sab196087 if (moddef != NULL) 14965088Sab196087 free(moddef); 14975088Sab196087 14985088Sab196087 if ((dl_hdl != NULL) && (dlclose(dl_hdl) != 0)) 14995088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE), 15005088Sab196087 dl_path, dlerror()); 15015088Sab196087 15025088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, format, s1_buf, s2_buf, s3_buf, s4_buf); 15035088Sab196087 #undef SCRBUFSIZE 15045088Sab196087 } 15055088Sab196087 15065088Sab196087 15075088Sab196087 /* 15085088Sab196087 * Load a module sharable object for load_module(). 15095088Sab196087 * 15105088Sab196087 * entry: 15115088Sab196087 * path - Path of file to open 15125088Sab196087 * moddef - If this function issues a non-returning error, it will 15135088Sab196087 * first return the memory referenced by moddef. This argument 15145088Sab196087 * is not used otherwise. 15155088Sab196087 * must_exist - If True, we consider it to be an error if the file given 15165088Sab196087 * by path does not exist. If False, no error is issued 15175088Sab196087 * and a NULL value is quietly returned. 15185088Sab196087 * 15195088Sab196087 * exit: 15205088Sab196087 * Returns a handle to the loaded object on success, or NULL if no 15215088Sab196087 * file was loaded. 15225088Sab196087 */ 15235088Sab196087 static void * 15245088Sab196087 load_module_dlopen(const char *path, MODLIST_T *moddef, int must_exist) 15255088Sab196087 { 15265088Sab196087 int fd; 15275088Sab196087 void *hdl; 15285088Sab196087 15295088Sab196087 /* 15305088Sab196087 * If the file is not required to exist, and it doesn't, then 15315088Sab196087 * we want to quietly return without an error. 15325088Sab196087 */ 15335088Sab196087 if (!must_exist) { 15345088Sab196087 fd = open(path, O_RDONLY); 15355088Sab196087 if (fd >= 0) { 15365088Sab196087 (void) close(fd); 15375088Sab196087 } else if (errno == ENOENT) { 15385088Sab196087 return (NULL); 15395088Sab196087 } 15405088Sab196087 } 15415088Sab196087 15425088Sab196087 if ((hdl = dlopen(path, RTLD_LAZY|RTLD_FIRST)) == NULL) 15435088Sab196087 load_module_err(moddef, NULL, NULL, 15445088Sab196087 MSG_INTL(MSG_ERR_CNTDLOPEN), path, dlerror(), NULL, NULL); 15455088Sab196087 15465088Sab196087 return (hdl); 15475088Sab196087 } 15485088Sab196087 15495088Sab196087 15505088Sab196087 /* 15515088Sab196087 * Sanity check option arguments to prevent common errors. The rest of 15525088Sab196087 * elfedit assumes these tests have been done, and does not check 15535088Sab196087 * again. 15545088Sab196087 */ 15555088Sab196087 static void 15565088Sab196087 validate_optarg(elfedit_cmd_optarg_t *optarg, int isopt, MODLIST_T *moddef, 15575088Sab196087 const char *mod_name, const char *cmd_name, 15585088Sab196087 void *dl_hdl, const char *dl_path) 15595088Sab196087 { 15605088Sab196087 #define FAIL(_msg) errmsg = _msg; goto fail 15615088Sab196087 15625088Sab196087 Msg errmsg; 15635088Sab196087 elfedit_cmd_oa_mask_t optmask = 0; 15645088Sab196087 15655088Sab196087 for (; optarg->oa_name != NULL; optarg++) { 15665088Sab196087 /* 15675088Sab196087 * If ELFEDIT_CMDOA_F_INHERIT is set: 15685088Sab196087 * - oa_name must be a value in the range of 15695088Sab196087 * known ELFEDIT_STDOA_ values. 15705088Sab196087 * - oa_help must be NULL 15715088Sab196087 * - ELFEDIT_CMDOA_F_INHERIT must be the only flag set 15725088Sab196087 */ 15735088Sab196087 if (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) { 15745088Sab196087 if ((((uintptr_t)optarg->oa_name) > 15755088Sab196087 ELFEDIT_NUM_STDOA) || 15765088Sab196087 (optarg->oa_help != 0) || 15775088Sab196087 (optarg->oa_flags != ELFEDIT_CMDOA_F_INHERIT)) 15785088Sab196087 /* 15795088Sab196087 * Can't use FAIL --- oa_name is not a valid 15805088Sab196087 * string, and load_module_err() looks at args. 15815088Sab196087 */ 15825088Sab196087 load_module_err(moddef, dl_hdl, dl_path, 15835088Sab196087 MSG_INTL(MSG_ERR_BADSTDOA), dl_path, 15845088Sab196087 mod_name, cmd_name, NULL); 15855088Sab196087 continue; 15865088Sab196087 } 15875088Sab196087 15885088Sab196087 if (isopt) { 15895088Sab196087 /* 15905088Sab196087 * Option name must start with a '-', and must 15915088Sab196087 * have at one following character. 15925088Sab196087 */ 15935088Sab196087 if (optarg->oa_name[0] != '-') { 15945088Sab196087 /* MSG_INTL(MSG_ERR_OPT_MODPRE) */ 15955088Sab196087 FAIL(MSG_ERR_OPT_MODPRE); 15965088Sab196087 } 15975088Sab196087 if (optarg->oa_name[1] == '\0') { 15985088Sab196087 /* MSG_INTL(MSG_ERR_OPT_MODLEN) */ 15995088Sab196087 FAIL(MSG_ERR_OPT_MODLEN); 16005088Sab196087 } 16015088Sab196087 16025088Sab196087 /* 16035088Sab196087 * oa_idmask must be 0, or it must have a single 16045088Sab196087 * bit set (a power of 2).oa_excmask must be 0 16055088Sab196087 * if oa_idmask is 0 16065088Sab196087 */ 16075088Sab196087 if (optarg->oa_idmask == 0) { 16085088Sab196087 if (optarg->oa_excmask != 0) { 16095088Sab196087 /* MSG_INTL(MSG_ERR_OPT_EXCMASKN0) */ 16105088Sab196087 FAIL(MSG_ERR_OPT_EXCMASKN0); 16115088Sab196087 } 16125088Sab196087 } else { 16135088Sab196087 if (elfedit_bits_set(optarg->oa_idmask, 16145088Sab196087 sizeof (optarg->oa_idmask)) != 1) { 16155088Sab196087 /* MSG_INTL(MSG_ERR_OPT_IDMASKPOW2) */ 16165088Sab196087 FAIL(MSG_ERR_OPT_IDMASKPOW2); 16175088Sab196087 } 16185088Sab196087 16195088Sab196087 /* Non-zero idmask must be unique */ 16205088Sab196087 if ((optarg->oa_idmask & optmask) != 0) { 16215088Sab196087 /* MSG_INTL(MSG_ERR_OPT_IDMASKUNIQ) */ 16225088Sab196087 FAIL(MSG_ERR_OPT_IDMASKUNIQ); 16235088Sab196087 } 16245088Sab196087 16255088Sab196087 /* Add this one to the overall mask */ 16265088Sab196087 optmask |= optarg->oa_idmask; 16275088Sab196087 } 16285088Sab196087 } else { 16295088Sab196087 /* 16305088Sab196087 * Argument name cannot start with a'-', and must 16315088Sab196087 * not be a null string. 16325088Sab196087 */ 16335088Sab196087 if (optarg->oa_name[0] == '-') { 16345088Sab196087 /* MSG_INTL(MSG_ERR_ARG_MODPRE) */ 16355088Sab196087 FAIL(MSG_ERR_ARG_MODPRE); 16365088Sab196087 } 16375088Sab196087 if (optarg->oa_name[1] == '\0') { 16385088Sab196087 /* MSG_INTL(MSG_ERR_ARG_MODLEN) */ 16395088Sab196087 FAIL(MSG_ERR_ARG_MODLEN); 16405088Sab196087 } 16415088Sab196087 16425088Sab196087 16435088Sab196087 /* oa_idmask and oa_excmask must both be 0 */ 16445088Sab196087 if ((optarg->oa_idmask != 0) || 16455088Sab196087 (optarg->oa_excmask != 0)) { 16465088Sab196087 /* MSG_INTL(MSG_ERR_ARG_MASKNOT0) */ 16475088Sab196087 FAIL(MSG_ERR_ARG_MASKNOT0); 16485088Sab196087 } 16495088Sab196087 16505088Sab196087 } 16515088Sab196087 16525088Sab196087 /* 16535088Sab196087 * If it takes a value, make sure that we are 16545088Sab196087 * processing options, because CMDOA_F_VALUE is not 16555088Sab196087 * allowed for plain arguments. Then check the following 16565088Sab196087 * item in the list: 16575088Sab196087 * - There must be a following item. 16585088Sab196087 * - oa_name must be non-NULL. This is the only field 16595088Sab196087 * that is used by elfedit. 16605088Sab196087 * - oa_help, oa_flags, oa_idmask, and oa_excmask 16615088Sab196087 * must be 0. 16625088Sab196087 */ 16635088Sab196087 if (optarg->oa_flags & ELFEDIT_CMDOA_F_VALUE) { 16645088Sab196087 elfedit_cmd_optarg_t *oa1 = optarg + 1; 16655088Sab196087 16665088Sab196087 if (!isopt) { 16675088Sab196087 /* MSG_INTL(MSG_ERR_ARG_CMDOA_VAL) */ 16685088Sab196087 FAIL(MSG_ERR_ARG_CMDOA_VAL); 16695088Sab196087 } 16705088Sab196087 16715088Sab196087 if ((optarg + 1)->oa_name == NULL) { 16725088Sab196087 /* MSG_INTL(MSG_ERR_BADMODOPTVAL) */ 16735088Sab196087 FAIL(MSG_ERR_BADMODOPTVAL); 16745088Sab196087 } 16755088Sab196087 16765088Sab196087 if (oa1->oa_name == NULL) { 16775088Sab196087 /* MSG_INTL(MSG_ERR_CMDOA_VALNAM) */ 16785088Sab196087 FAIL(MSG_ERR_CMDOA_VALNAM); 16795088Sab196087 } 16805088Sab196087 if ((oa1->oa_help != NULL) || (oa1->oa_flags != 0) || 16815088Sab196087 (oa1->oa_idmask != 0) || (oa1->oa_excmask != 0)) { 16825088Sab196087 /* MSG_INTL(MSG_ERR_CMDOA_VALNOT0) */ 16835088Sab196087 FAIL(MSG_ERR_CMDOA_VALNOT0); 16845088Sab196087 } 16855088Sab196087 optarg++; 16865088Sab196087 } 16875088Sab196087 } 16885088Sab196087 16895088Sab196087 16905088Sab196087 return; 16915088Sab196087 16925088Sab196087 fail: 16935088Sab196087 load_module_err(moddef, dl_hdl, dl_path, MSG_INTL(errmsg), 16945088Sab196087 dl_path, mod_name, cmd_name, optarg->oa_name); 16955088Sab196087 } 16965088Sab196087 16975088Sab196087 /* 16985088Sab196087 * Look up the specified module, loading the module if necessary, 16995088Sab196087 * and return its definition, or NULL on failure. 17005088Sab196087 * 17015088Sab196087 * entry: 17025088Sab196087 * name - Name of module to load. If name contains a '/' character or has 17035088Sab196087 * a ".so" suffix, then it is taken to be an absolute file path, 17045088Sab196087 * and is used directly as is. If name does not contain a '/' 17055088Sab196087 * character, then we look for it against the locations in 17065088Sab196087 * the module path, addint the '.so' suffix, and taking the first 17075088Sab196087 * one we find. 17085088Sab196087 * must_exist - If True, we consider it to be an error if we are unable 17095088Sab196087 * to locate a file to load and the module does not already exist. 17105088Sab196087 * If False, NULL is returned quietly in this case. 17115088Sab196087 * allow_abs - True if absolute paths are allowed. False to disallow 17125088Sab196087 * them. 17135088Sab196087 * 17145088Sab196087 * note: 17155088Sab196087 * If the path is absolute, then we load the file and take the module 17165088Sab196087 * name from the data returned by its elfedit_init() function. If a 17175088Sab196087 * module of that name is already loaded, it is unloaded and replaced 17185088Sab196087 * with the new one. 17195088Sab196087 * 17205088Sab196087 * If the path is non absolute, then we check to see if the module has 17215088Sab196087 * already been loaded, and if so, we return that module definition. 17225088Sab196087 * In this case, nothing new is loaded. If the module has not been loaded, 17235088Sab196087 * we search the path for it and load it. If the module name provided 17245088Sab196087 * by the elfedit_init() function does not match the name of the file, 17255088Sab196087 * an error results. 17265088Sab196087 */ 17275088Sab196087 elfeditGC_module_t * 17285088Sab196087 elfedit_load_module(const char *name, int must_exist, int allow_abs) 17295088Sab196087 { 17305088Sab196087 elfedit_init_func_t *init_func; 17315088Sab196087 elfeditGC_module_t *mod; 17325088Sab196087 MODLIST_T *moddef, *insdef; 17335088Sab196087 const char *path; 17345088Sab196087 char path_buf[PATH_MAX + 1]; 17355088Sab196087 void *hdl; 17365088Sab196087 size_t i; 17375088Sab196087 int is_abs_path; 17385088Sab196087 elfeditGC_cmd_t *cmd; 17395088Sab196087 17405088Sab196087 /* 17415088Sab196087 * If the name includes a .so suffix, or has any '/' characters, 17425088Sab196087 * then it is an absolute path that we use as is to load the named 17435088Sab196087 * file. Otherwise, we iterate over the path, adding the .so suffix 17445088Sab196087 * and load the first file that matches. 17455088Sab196087 */ 17465088Sab196087 is_abs_path = (path_is_so(name) != NULL) || 17475088Sab196087 (name != elfedit_basename(name, NULL, NULL, 0)); 17485088Sab196087 17495088Sab196087 if (is_abs_path && !allow_abs) 17505088Sab196087 load_module_err(NULL, NULL, NULL, 17515088Sab196087 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL); 17525088Sab196087 17535088Sab196087 /* 17545088Sab196087 * If this is a non-absolute path, search for the module already 17555088Sab196087 * having been loaded, and return it if so. 17565088Sab196087 */ 17575088Sab196087 if (!is_abs_path) { 17585088Sab196087 moddef = module_loaded(name, &insdef); 17595088Sab196087 if (moddef != NULL) 17605088Sab196087 return (moddef->ml_mod); 17615088Sab196087 /* 17625088Sab196087 * As a result of module_loaded(), insdef now contains the 17635088Sab196087 * immediate predecessor node for the new one, or NULL if 17645088Sab196087 * it goes at the front. In the absolute-path case, we take 17655088Sab196087 * care of this below, after the sharable object is loaded. 17665088Sab196087 */ 17675088Sab196087 } 17685088Sab196087 17695088Sab196087 /* 17705088Sab196087 * malloc() a module definition block before trying to dlopen(). 17715088Sab196087 * Doing things in the other order can cause the dlopen()'d object 17725088Sab196087 * to leak: If elfedit_malloc() fails, it can cause a jump to the 17735088Sab196087 * outer command loop without returning to the caller. Hence, 17745088Sab196087 * there will be no opportunity to clean up. Allocaing the module 17755088Sab196087 * first allows us to free it if necessary. 17765088Sab196087 */ 17775088Sab196087 moddef = elfedit_malloc(MSG_INTL(MSG_ALLOC_MODDEF), 17785088Sab196087 sizeof (*moddef) + PATH_MAX + 1); 17795088Sab196087 moddef->ml_path = ((char *)moddef) + sizeof (*moddef); 17805088Sab196087 17815088Sab196087 if (is_abs_path) { 17825088Sab196087 path = name; 17835088Sab196087 hdl = load_module_dlopen(name, moddef, must_exist); 17845088Sab196087 } else { 17855088Sab196087 hdl = NULL; 17865088Sab196087 path = path_buf; 17875088Sab196087 for (i = 0; i < state.modpath.n; i++) { 17885088Sab196087 if (snprintf(path_buf, sizeof (path_buf), 17895088Sab196087 MSG_ORIG(MSG_FMT_BLDSOPATH), state.modpath.seg[i], 17905088Sab196087 name) > sizeof (path_buf)) 17915088Sab196087 load_module_err(moddef, NULL, NULL, 17925088Sab196087 MSG_INTL(MSG_ERR_PATHTOOLONG), 17935088Sab196087 state.modpath.seg[i], name, NULL, NULL); 17945088Sab196087 hdl = load_module_dlopen(path, moddef, 0); 17955088Sab196087 } 17965088Sab196087 if (must_exist && (hdl == NULL)) 17975088Sab196087 load_module_err(moddef, NULL, NULL, 17985088Sab196087 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL); 17995088Sab196087 } 18005088Sab196087 18015088Sab196087 if (hdl == NULL) { 18025088Sab196087 free(moddef); 18035088Sab196087 return (NULL); 18045088Sab196087 } 18055088Sab196087 18065088Sab196087 if (state.elf.elfclass == ELFCLASS32) { 18075088Sab196087 init_func = (elfedit_init_func_t *) 18085088Sab196087 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT32)); 18095088Sab196087 } else { 18105088Sab196087 init_func = (elfedit_init_func_t *) 18115088Sab196087 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT64)); 18125088Sab196087 } 18135088Sab196087 if (init_func == NULL) 18145088Sab196087 load_module_err(moddef, hdl, path, 18155088Sab196087 MSG_INTL(MSG_ERR_SONOTMOD), path, NULL, NULL, NULL); 18165088Sab196087 18175088Sab196087 /* 18185088Sab196087 * Note that the init function will be passing us an 18195088Sab196087 * elfedit[32|64]_module_t pointer, which we cast to the 18205088Sab196087 * generic module pointer type in order to be able to manage 18215088Sab196087 * either type with one set of code. 18225088Sab196087 */ 18235088Sab196087 if (!(mod = (elfeditGC_module_t *)(* init_func)(ELFEDIT_VER_CURRENT))) 18245088Sab196087 load_module_err(moddef, hdl, path, 18255088Sab196087 MSG_INTL(MSG_ERR_BADMODLOAD), path, NULL, NULL, NULL); 18265088Sab196087 18275088Sab196087 /* 18285088Sab196087 * Enforce some rules, to help module developers: 18295088Sab196087 * - The primary name of a command must not be 18305088Sab196087 * the empty string (""). 18315088Sab196087 * - Options must start with a '-' followed by at least 18325088Sab196087 * one character. 18335088Sab196087 * - Arguments and options must be well formed. 18345088Sab196087 */ 18355088Sab196087 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) { 18365088Sab196087 if (**cmd->cmd_name == '\0') 18375088Sab196087 load_module_err(moddef, hdl, path, 18385088Sab196087 MSG_INTL(MSG_ERR_NULLPRICMDNAM), mod->mod_name, 18395088Sab196087 NULL, NULL, NULL); 18405088Sab196087 18415088Sab196087 if (cmd->cmd_args != NULL) 18425088Sab196087 validate_optarg(cmd->cmd_args, 0, moddef, mod->mod_name, 18435088Sab196087 cmd->cmd_name[0], hdl, path); 18445088Sab196087 if (cmd->cmd_opt != NULL) 18455088Sab196087 validate_optarg(cmd->cmd_opt, 1, moddef, mod->mod_name, 18465088Sab196087 cmd->cmd_name[0], hdl, path); 18475088Sab196087 } 18485088Sab196087 18495088Sab196087 /* 18505088Sab196087 * Check the name the module provides. How we handle this depends 18515088Sab196087 * on whether the path is absolute or the result of a path search. 18525088Sab196087 */ 18535088Sab196087 if (is_abs_path) { 18545088Sab196087 MODLIST_T *old_moddef = module_loaded(mod->mod_name, &insdef); 18555088Sab196087 18565088Sab196087 if (old_moddef != NULL) { /* Replace existing */ 18575088Sab196087 free(moddef); /* Rare case: Don't need it */ 18585088Sab196087 /* 18595088Sab196087 * Be sure we don't unload builtin modules! 18605088Sab196087 * These have a NULL dl_hdl field. 18615088Sab196087 */ 18625088Sab196087 if (old_moddef->ml_dl_hdl == NULL) 18635088Sab196087 load_module_err(NULL, hdl, path, 18645088Sab196087 MSG_INTL(MSG_ERR_CNTULSMOD), 18655088Sab196087 old_moddef->ml_mod->mod_name, NULL, 18665088Sab196087 NULL, NULL); 18675088Sab196087 18685088Sab196087 /* Unload existing */ 18695088Sab196087 if (dlclose(old_moddef->ml_dl_hdl) != 0) 18705088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 18715088Sab196087 MSG_INTL(MSG_ERR_CNTDLCLOSE), 18725088Sab196087 old_moddef->ml_path, dlerror()); 18735088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 18745088Sab196087 MSG_INTL(MSG_DEBUG_MODUNLOAD), 18755088Sab196087 old_moddef->ml_mod->mod_name, old_moddef->ml_path); 18765088Sab196087 old_moddef->ml_mod = mod; 18775088Sab196087 old_moddef->ml_dl_hdl = hdl; 18785088Sab196087 (void) strlcpy((char *)old_moddef->ml_path, path, 18795088Sab196087 PATH_MAX + 1); 18805088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 18815088Sab196087 MSG_INTL(MSG_DEBUG_MODLOAD), 18825088Sab196087 old_moddef->ml_mod->mod_name, path); 18835088Sab196087 return (old_moddef->ml_mod); 18845088Sab196087 } 18855088Sab196087 /* 18865088Sab196087 * insdef now contains the insertion point for the absolute 18875088Sab196087 * path case. 18885088Sab196087 */ 18895088Sab196087 } else { 18905088Sab196087 /* If the names don't match, then error */ 18915088Sab196087 if (strcasecmp(name, mod->mod_name) != 0) 18925088Sab196087 load_module_err(moddef, hdl, path, 18935088Sab196087 MSG_INTL(MSG_ERR_BADMODNAME), 18945088Sab196087 mod->mod_name, name, path, NULL); 18955088Sab196087 } 18965088Sab196087 18975088Sab196087 /* 18985088Sab196087 * Link module into the module list. If insdef is NULL, 18995088Sab196087 * it goes at the head. If insdef is non-NULL, it goes immediately 19005088Sab196087 * after 19015088Sab196087 */ 19025088Sab196087 if (insdef == NULL) { 19035088Sab196087 moddef->ml_next = state.modlist; 19045088Sab196087 state.modlist = moddef; 19055088Sab196087 } else { 19065088Sab196087 moddef->ml_next = insdef->ml_next; 19075088Sab196087 insdef->ml_next = moddef; 19085088Sab196087 } 19095088Sab196087 moddef->ml_mod = mod; 19105088Sab196087 moddef->ml_dl_hdl = hdl; 19115088Sab196087 (void) strlcpy((char *)moddef->ml_path, path, PATH_MAX + 1); 19125088Sab196087 19135088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODLOAD), 19145088Sab196087 moddef->ml_mod->mod_name, path); 19155088Sab196087 19165088Sab196087 return (moddef->ml_mod); 19175088Sab196087 } 19185088Sab196087 19195088Sab196087 19205088Sab196087 /* 19215088Sab196087 * Unload the specified module 19225088Sab196087 */ 19235088Sab196087 void 19245088Sab196087 elfedit_unload_module(const char *name) 19255088Sab196087 { 19265088Sab196087 MODLIST_T *moddef, *insdef; 19275088Sab196087 19285088Sab196087 moddef = module_loaded(name, &insdef); 19295088Sab196087 if (moddef == NULL) 19305088Sab196087 return; 19315088Sab196087 19325088Sab196087 /* Built in modules cannot be unloaded. They have a NULL dl_hdl field */ 19335088Sab196087 if (moddef->ml_dl_hdl == NULL) 19345088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTULSMOD), 19355088Sab196087 moddef->ml_mod->mod_name); 19365088Sab196087 19375088Sab196087 /* 19385088Sab196087 * When we unload it, the name string goes with it. So 19395088Sab196087 * announce it while we still can without having to make a copy. 19405088Sab196087 */ 19415088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODUNLOAD), 19425088Sab196087 moddef->ml_mod->mod_name, moddef->ml_path); 19435088Sab196087 19445088Sab196087 /* 19455088Sab196087 * Close it before going further. On failure, we'll jump, and the 19465088Sab196087 * record will remain in the module list. On success, 19475088Sab196087 * we'll retain control, and can safely remove it. 19485088Sab196087 */ 19495088Sab196087 if (dlclose(moddef->ml_dl_hdl) != 0) 19505088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE), 19515088Sab196087 moddef->ml_path, dlerror()); 19525088Sab196087 19535088Sab196087 /* Unlink the record from the module list */ 19545088Sab196087 if (insdef == NULL) 19555088Sab196087 state.modlist = moddef->ml_next; 19565088Sab196087 else 19575088Sab196087 insdef->ml_next = moddef->ml_next; 19585088Sab196087 19595088Sab196087 /* Release the memory */ 19605088Sab196087 free(moddef); 19615088Sab196087 } 19625088Sab196087 19635088Sab196087 19645088Sab196087 /* 19655088Sab196087 * Load all sharable objects found in the specified directory. 19665088Sab196087 * 19675088Sab196087 * entry: 19685088Sab196087 * dirpath - Path of directory to process. 19695088Sab196087 * must_exist - If True, it is an error if diropen() fails to open 19705088Sab196087 * the given directory. Of False, we quietly ignore it and return. 19715088Sab196087 * abs_path - If True, files are loaded using their literal paths. 19725088Sab196087 * If False, their module name is extracted from the dirpath 19735088Sab196087 * and a path based search is used to locate it. 19745088Sab196087 */ 19755088Sab196087 void 19765088Sab196087 elfedit_load_moddir(const char *dirpath, int must_exist, int abs_path) 19775088Sab196087 { 19785088Sab196087 char path[PATH_MAX + 1]; 19795088Sab196087 DIR *dir; 19805088Sab196087 struct dirent *dp; 19815088Sab196087 const char *tail; 19825088Sab196087 19835088Sab196087 dir = opendir(dirpath); 19845088Sab196087 if (dir == NULL) { 19855088Sab196087 int err = errno; 19865088Sab196087 19875088Sab196087 if (!must_exist && (err == ENOENT)) 19885088Sab196087 return; 19895088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNDIR), 19905088Sab196087 dirpath, strerror(err)); 19915088Sab196087 /*NOTREACHED*/ 19925088Sab196087 } 19935088Sab196087 19945088Sab196087 while (dp = readdir(dir)) { 19955088Sab196087 if ((tail = path_is_so(dp->d_name)) != NULL) { 19965088Sab196087 if (abs_path) { 19975088Sab196087 (void) snprintf(path, sizeof (path), 19985088Sab196087 MSG_ORIG(MSG_FMT_BLDPATH), dirpath, 19995088Sab196087 dp->d_name); 20005088Sab196087 } else { 20015088Sab196087 (void) elfedit_basename(dp->d_name, tail, 20025088Sab196087 path, sizeof (path)); 20035088Sab196087 } 20045088Sab196087 (void) elfedit_load_module(path, must_exist, 1); 20055088Sab196087 } 20065088Sab196087 } 20075088Sab196087 (void) closedir(dir); 20085088Sab196087 } 20095088Sab196087 20105088Sab196087 20115088Sab196087 /* 20125088Sab196087 * Follow the module load path, and load the first module found for each 20135088Sab196087 * given name. 20145088Sab196087 */ 20155088Sab196087 void 20165088Sab196087 elfedit_load_modpath(void) 20175088Sab196087 { 20185088Sab196087 size_t i; 20195088Sab196087 20205088Sab196087 for (i = 0; i < state.modpath.n; i++) 20215088Sab196087 elfedit_load_moddir(state.modpath.seg[i], 0, 0); 20225088Sab196087 } 20235088Sab196087 20245088Sab196087 /* 20255088Sab196087 * Given a module definition, look for the specified command. 20265088Sab196087 * Returns the command if found, and NULL otherwise. 20275088Sab196087 */ 20285088Sab196087 static elfeditGC_cmd_t * 20295088Sab196087 find_cmd(elfeditGC_module_t *mod, const char *name) 20305088Sab196087 { 20315088Sab196087 elfeditGC_cmd_t *cmd; 20325088Sab196087 const char **cmd_name; 20335088Sab196087 20345088Sab196087 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) 20355088Sab196087 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++) 20365088Sab196087 if (strcasecmp(name, *cmd_name) == 0) { 20375088Sab196087 if (cmd_name != cmd->cmd_name) 20385088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 20395088Sab196087 MSG_INTL(MSG_DEBUG_CMDALIAS), 20405088Sab196087 mod->mod_name, *cmd_name, 20415088Sab196087 mod->mod_name, *cmd->cmd_name); 20425088Sab196087 return (cmd); 20435088Sab196087 } 20445088Sab196087 20455088Sab196087 return (NULL); 20465088Sab196087 } 20475088Sab196087 20485088Sab196087 20495088Sab196087 /* 20505088Sab196087 * Given a command name, return its command definition. 20515088Sab196087 * 20525088Sab196087 * entry: 20535088Sab196087 * name - Command to be looked up 20545088Sab196087 * must_exist - If True, we consider it to be an error if the command 20555088Sab196087 * does not exist. If False, NULL is returned quietly in 20565088Sab196087 * this case. 20575088Sab196087 * mod_ret - NULL, or address of a variable to receive the 20585088Sab196087 * module definition block of the module containing 20595088Sab196087 * the command. 20605088Sab196087 * 20615088Sab196087 * exit: 20625088Sab196087 * On success, returns a pointer to the command definition, and 20635088Sab196087 * if mod_ret is non-NULL, *mod_ret receives a pointer to the 20645088Sab196087 * module definition. On failure, must_exist determines the 20655088Sab196087 * action taken: If must_exist is True, an error is issued and 20665088Sab196087 * control does not return to the caller. If must_exist is False, 20675088Sab196087 * NULL is quietly returned. 20685088Sab196087 * 20695088Sab196087 * note: 20705088Sab196087 * A ':' in name is used to delimit the module and command names. 20715088Sab196087 * If it is omitted, or if it is the first non-whitespace character 20725088Sab196087 * in the name, then the built in sys: module is implied. 20735088Sab196087 */ 20745088Sab196087 elfeditGC_cmd_t * 20755088Sab196087 elfedit_find_command(const char *name, int must_exist, 20765088Sab196087 elfeditGC_module_t **mod_ret) 20775088Sab196087 { 20785088Sab196087 elfeditGC_module_t *mod; 20795088Sab196087 const char *mod_str; 20805088Sab196087 const char *cmd_str; 20815088Sab196087 char mod_buf[ELFEDIT_MAXMODNAM + 1]; 20825088Sab196087 size_t n; 20835088Sab196087 elfeditGC_cmd_t *cmd; 20845088Sab196087 20855088Sab196087 20865088Sab196087 cmd_str = strstr(name, MSG_ORIG(MSG_STR_COLON)); 20875088Sab196087 if (cmd_str == NULL) { /* No module name -> sys: */ 20885088Sab196087 mod_str = MSG_ORIG(MSG_MOD_SYS); 20895088Sab196087 cmd_str = name; 20905088Sab196087 } else if (cmd_str == name) { /* Empty module name -> sys: */ 20915088Sab196087 mod_str = MSG_ORIG(MSG_MOD_SYS); 20925088Sab196087 cmd_str++; /* Skip the colon */ 20935088Sab196087 } else { /* Have both module and command */ 20945088Sab196087 n = cmd_str - name; 20955088Sab196087 if (n >= sizeof (mod_buf)) { 20965088Sab196087 if (must_exist) 20975088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 20985088Sab196087 MSG_INTL(MSG_ERR_MODNAMTOOLONG), name); 20995088Sab196087 return (NULL); 21005088Sab196087 } 21015088Sab196087 (void) strlcpy(mod_buf, name, n + 1); 21025088Sab196087 mod_str = mod_buf; 21035088Sab196087 cmd_str++; 21045088Sab196087 } 21055088Sab196087 21065088Sab196087 /* Lookup/load module. Won't return on error */ 21075088Sab196087 mod = elfedit_load_module(mod_str, must_exist, 0); 21085088Sab196087 if (mod == NULL) 21095088Sab196087 return (NULL); 21105088Sab196087 21115088Sab196087 /* Locate the command */ 21125088Sab196087 cmd = find_cmd(mod, cmd_str); 21135088Sab196087 if (cmd == NULL) { 21145088Sab196087 if (must_exist) { 21155088Sab196087 /* 21165088Sab196087 * Catch empty command in order to provide 21175088Sab196087 * a better error message. 21185088Sab196087 */ 21195088Sab196087 if (*cmd_str == '\0') { 21205088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 21215088Sab196087 MSG_INTL(MSG_ERR_MODNOCMD), mod_str); 21225088Sab196087 } else { 21235088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 21245088Sab196087 MSG_INTL(MSG_ERR_UNRECCMD), 21255088Sab196087 mod_str, cmd_str); 21265088Sab196087 } 21275088Sab196087 } 21285088Sab196087 } else { 21295088Sab196087 if (mod_ret != NULL) 21305088Sab196087 *mod_ret = mod; 21315088Sab196087 } 21325088Sab196087 return (cmd); 21335088Sab196087 } 21345088Sab196087 21355088Sab196087 21365088Sab196087 /* 21375088Sab196087 * Release all user command blocks found on state.ucmd 21385088Sab196087 */ 21395088Sab196087 static void 21405088Sab196087 free_user_cmds(void) 21415088Sab196087 { 21425088Sab196087 USER_CMD_T *next; 21435088Sab196087 21445088Sab196087 while (state.ucmd.list) { 21455088Sab196087 next = state.ucmd.list->ucmd_next; 21465088Sab196087 free(state.ucmd.list); 21475088Sab196087 state.ucmd.list = next; 21485088Sab196087 } 21495088Sab196087 state.ucmd.tail = NULL; 21505088Sab196087 state.ucmd.n = 0; 21515088Sab196087 state.cur_cmd = NULL; 21525088Sab196087 } 21535088Sab196087 21545088Sab196087 21555088Sab196087 /* 21565088Sab196087 * Process all user command blocks found on state.ucmd, and then 21575088Sab196087 * remove them from the list. 21585088Sab196087 */ 21595088Sab196087 static void 21605088Sab196087 dispatch_user_cmds() 21615088Sab196087 { 21625088Sab196087 USER_CMD_T *ucmd; 21635088Sab196087 elfedit_cmdret_t cmd_ret; 21645088Sab196087 21655088Sab196087 ucmd = state.ucmd.list; 21665088Sab196087 if (ucmd) { 21675088Sab196087 /* Do them, in order */ 21685088Sab196087 for (; ucmd; ucmd = ucmd->ucmd_next) { 21695088Sab196087 state.cur_cmd = ucmd; 21705088Sab196087 if (!state.msg_jbuf.active) 21715088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 21725088Sab196087 MSG_INTL(MSG_DEBUG_EXECCMD), 21735088Sab196087 ucmd->ucmd_orig_str); 21745088Sab196087 /* 21755088Sab196087 * The cmd_func field is the generic definition. 21765088Sab196087 * We need to cast it to the type that matches 21775088Sab196087 * the proper ELFCLASS before calling it. 21785088Sab196087 */ 21795088Sab196087 if (state.elf.elfclass == ELFCLASS32) { 21805088Sab196087 elfedit32_cmd_func_t *cmd_func = 21815088Sab196087 (elfedit32_cmd_func_t *) 21825088Sab196087 ucmd->ucmd_cmd->cmd_func; 21835088Sab196087 21845088Sab196087 cmd_ret = (* cmd_func)(state.elf.obj_state.s32, 21855088Sab196087 ucmd->ucmd_argc, ucmd->ucmd_argv); 21865088Sab196087 } else { 21875088Sab196087 elfedit64_cmd_func_t *cmd_func = 21885088Sab196087 (elfedit64_cmd_func_t *) 21895088Sab196087 ucmd->ucmd_cmd->cmd_func; 21905088Sab196087 21915088Sab196087 cmd_ret = (* cmd_func)(state.elf.obj_state.s64, 21925088Sab196087 ucmd->ucmd_argc, ucmd->ucmd_argv); 21935088Sab196087 } 21945088Sab196087 state.cur_cmd = NULL; 21955088Sab196087 /* If a pager was started, wrap it up */ 21965088Sab196087 elfedit_pager_cleanup(); 21975088Sab196087 21985088Sab196087 switch (cmd_ret) { 21995088Sab196087 case ELFEDIT_CMDRET_MOD: 22005088Sab196087 /* 22015088Sab196087 * Command modified the output ELF image, 22025088Sab196087 * mark the file as needing a flush to disk. 22035088Sab196087 */ 22045088Sab196087 state.file.dirty = 1; 22055088Sab196087 break; 22065088Sab196087 case ELFEDIT_CMDRET_FLUSH: 22075088Sab196087 /* 22085088Sab196087 * Command flushed the output file, 22095088Sab196087 * clear the dirty bit. 22105088Sab196087 */ 22115088Sab196087 state.file.dirty = 0; 22125088Sab196087 } 22135088Sab196087 } 22145088Sab196087 free_user_cmds(); 22155088Sab196087 } 22165088Sab196087 } 22175088Sab196087 22185088Sab196087 22195088Sab196087 /* 22205892Sab196087 * Given the pointer to the character following a '\' character in 22215892Sab196087 * a C style literal, return the ASCII character code it represents, 22225892Sab196087 * and advance the string pointer to the character following the last 22235892Sab196087 * character in the escape sequence. 22245892Sab196087 * 22255892Sab196087 * entry: 22265892Sab196087 * str - Address of string pointer to first character following 22275892Sab196087 * the backslash. 22285892Sab196087 * 22295892Sab196087 * exit: 22305892Sab196087 * If the character is not valid, an error is thrown and this routine 22315892Sab196087 * does not return to its caller. Otherwise, it returns the ASCII 22325892Sab196087 * code for the translated character, and *str has been advanced. 22335892Sab196087 */ 22345892Sab196087 static int 22355892Sab196087 translate_c_esc(char **str) 22365892Sab196087 { 22375892Sab196087 char *s = *str; 22385892Sab196087 int ch; 22395892Sab196087 int i; 22405892Sab196087 22415892Sab196087 ch = *s++; 22425892Sab196087 switch (ch) { 22435892Sab196087 case 'a': 22445892Sab196087 ch = '\a'; 22455892Sab196087 break; 22465892Sab196087 case 'b': 22475892Sab196087 ch = '\b'; 22485892Sab196087 break; 22495892Sab196087 case 'f': 22505892Sab196087 ch = '\f'; 22515892Sab196087 break; 22525892Sab196087 case 'n': 22535892Sab196087 ch = '\n'; 22545892Sab196087 break; 22555892Sab196087 case 'r': 22565892Sab196087 ch = '\r'; 22575892Sab196087 break; 22585892Sab196087 case 't': 22595892Sab196087 ch = '\t'; 22605892Sab196087 break; 22615892Sab196087 case 'v': 22625892Sab196087 ch = '\v'; 22635892Sab196087 break; 22645892Sab196087 22655892Sab196087 case '0': 22665892Sab196087 case '1': 22675892Sab196087 case '2': 22685892Sab196087 case '3': 22695892Sab196087 case '4': 22705892Sab196087 case '5': 22715892Sab196087 case '6': 22725892Sab196087 case '7': 22735892Sab196087 /* Octal constant: There can be up to 3 digits */ 22745892Sab196087 ch -= '0'; 22755892Sab196087 for (i = 0; i < 2; i++) { 22765892Sab196087 if ((*s < '0') || (*s > '7')) 22775892Sab196087 break; 22785892Sab196087 ch = (ch << 3) + (*s++ - '0'); 22795892Sab196087 } 22805892Sab196087 break; 22815892Sab196087 22825892Sab196087 /* 22835892Sab196087 * There are some cases where ch already has the desired value. 22845892Sab196087 * These cases exist simply to remove the special meaning that 22855892Sab196087 * character would otherwise have. We need to match them to 22865892Sab196087 * prevent them from falling into the default error case. 22875892Sab196087 */ 22885892Sab196087 case '\\': 22895892Sab196087 case '\'': 22905892Sab196087 case '"': 22915892Sab196087 break; 22925892Sab196087 22935892Sab196087 default: 22945892Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADCESC), ch); 22955892Sab196087 break; 22965892Sab196087 } 22975892Sab196087 22985892Sab196087 *str = s; 22995892Sab196087 return (ch); 23005892Sab196087 } 23015892Sab196087 23025892Sab196087 23035892Sab196087 /* 23045088Sab196087 * Prepare a GETTOK_STATE struct for gettok(). 23055088Sab196087 * 23065088Sab196087 * entry: 23075088Sab196087 * gettok_state - gettok state block to use 23085088Sab196087 * str - Writable buffer to tokenize. Note that gettok() 23095088Sab196087 * is allowed to change the contents of this buffer. 23105088Sab196087 * inc_null_final - If the line ends in whitespace instead of 23115088Sab196087 * immediately hitting a NULL, and inc_null_final is TRUE, 23125088Sab196087 * then a null final token is generated. Otherwise trailing 23135088Sab196087 * whitespace is ignored. 23145088Sab196087 */ 23155088Sab196087 static void 23165088Sab196087 gettok_init(GETTOK_STATE *gettok_state, char *buf, int inc_null_final) 23175088Sab196087 { 23185088Sab196087 gettok_state->gtok_buf = gettok_state->gtok_cur_buf = buf; 23195088Sab196087 gettok_state->gtok_inc_null_final = inc_null_final; 23205088Sab196087 gettok_state->gtok_null_seen = 0; 23215088Sab196087 } 23225088Sab196087 23235088Sab196087 23245088Sab196087 /* 23255088Sab196087 * Locate the next token from the buffer. 23265088Sab196087 * 23275088Sab196087 * entry: 23285088Sab196087 * gettok_state - State of gettok() operation. Initialized 23295088Sab196087 * by gettok_init(), and passed to gettok(). 23305088Sab196087 * 23315088Sab196087 * exit: 23325088Sab196087 * If a token is found, gettok_state->gtok_last_token is filled in 23335088Sab196087 * with the details and True (1) is returned. If no token is found, 23345088Sab196087 * False (1) is returned, and the contents of 23355088Sab196087 * gettok_state->gtok_last_token are undefined. 23365088Sab196087 * 23375088Sab196087 * note: 23385088Sab196087 * - The token returned references the memory in gettok_state->gtok_buf. 23395088Sab196087 * The caller should not modify the buffer until all such 23405088Sab196087 * pointers have been discarded. 23415088Sab196087 * - This routine will modify the contents of gettok_state->gtok_buf 23425088Sab196087 * as necessary to remove quotes and eliminate escape 23435088Sab196087 * (\)characters. 23445088Sab196087 */ 23455088Sab196087 static int 23465088Sab196087 gettok(GETTOK_STATE *gettok_state) 23475088Sab196087 { 23485088Sab196087 char *str = gettok_state->gtok_cur_buf; 23495088Sab196087 char *look; 23505088Sab196087 int quote_ch = '\0'; 23515088Sab196087 23525088Sab196087 /* Skip leading whitespace */ 23535088Sab196087 while (isspace(*str)) 23545088Sab196087 str++; 23555088Sab196087 23565088Sab196087 if (*str == '\0') { 23575088Sab196087 /* 23585088Sab196087 * If user requested it, and there was whitespace at the 23595088Sab196087 * end, then generate one last null token. 23605088Sab196087 */ 23615088Sab196087 if (gettok_state->gtok_inc_null_final && 23625088Sab196087 !gettok_state->gtok_null_seen) { 23635088Sab196087 gettok_state->gtok_inc_null_final = 0; 23645088Sab196087 gettok_state->gtok_null_seen = 1; 23655088Sab196087 gettok_state->gtok_last_token.tok_str = str; 23665088Sab196087 gettok_state->gtok_last_token.tok_len = 0; 23675088Sab196087 gettok_state->gtok_last_token.tok_line_off = 23685088Sab196087 str - gettok_state->gtok_buf; 23695088Sab196087 return (1); 23705088Sab196087 } 23715088Sab196087 gettok_state->gtok_null_seen = 1; 23725088Sab196087 return (0); 23735088Sab196087 } 23745088Sab196087 23755088Sab196087 /* 23765088Sab196087 * Read token: The standard delimiter is whitespace, but 23775088Sab196087 * we honor either single or double quotes. Also, we honor 23785088Sab196087 * backslash escapes. 23795088Sab196087 */ 23805088Sab196087 gettok_state->gtok_last_token.tok_str = look = str; 23815088Sab196087 gettok_state->gtok_last_token.tok_line_off = 23825088Sab196087 look - gettok_state->gtok_buf; 23835088Sab196087 for (; *look; look++) { 23845088Sab196087 if (*look == quote_ch) { /* Terminates active quote */ 23855088Sab196087 quote_ch = '\0'; 23865088Sab196087 continue; 23875088Sab196087 } 23885088Sab196087 23895088Sab196087 if (quote_ch == '\0') { /* No quote currently active */ 23905088Sab196087 if ((*look == '\'') || (*look == '"')) { 23915088Sab196087 quote_ch = *look; /* New active quote */ 23925088Sab196087 continue; 23935088Sab196087 } 23945088Sab196087 if (isspace(*look)) 23955088Sab196087 break; 23965088Sab196087 } 23975088Sab196087 23985892Sab196087 /* 23995892Sab196087 * The semantics of the backslash character depends on 24005892Sab196087 * the quote style in use: 24015892Sab196087 * - Within single quotes, backslash is not 24025892Sab196087 * an escape character, and is taken literally. 24035892Sab196087 * - If outside of quotes, the backslash is an escape 24045892Sab196087 * character. The backslash is ignored and the 24055892Sab196087 * following character is taken literally, losing 24065892Sab196087 * any special properties it normally has. 24075892Sab196087 * - Within double quotes, backslash works like a 24085892Sab196087 * backslash escape within a C literal. Certain 24095892Sab196087 * escapes are recognized and replaced with their 24105892Sab196087 * special character. Any others are an error. 24115892Sab196087 */ 24125088Sab196087 if (*look == '\\') { 24135892Sab196087 if (quote_ch == '\'') { 24145892Sab196087 *str++ = *look; 24155892Sab196087 continue; 24165892Sab196087 } 24175892Sab196087 24185088Sab196087 look++; 24195892Sab196087 if (*look == '\0') { /* Esc applied to NULL term? */ 24205892Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 24215892Sab196087 MSG_INTL(MSG_ERR_ESCEOL)); 24225892Sab196087 /*NOTREACHED*/ 24235892Sab196087 } 24245892Sab196087 24255892Sab196087 if (quote_ch == '"') { 24265892Sab196087 *str++ = translate_c_esc(&look); 24275892Sab196087 look--; /* for() will advance by 1 */ 24285892Sab196087 continue; 24295892Sab196087 } 24305088Sab196087 } 24315088Sab196087 24325088Sab196087 if (look != str) 24335088Sab196087 *str = *look; 24345088Sab196087 str++; 24355088Sab196087 } 24365892Sab196087 24375892Sab196087 /* Don't allow unterminated quoted tokens */ 24385892Sab196087 if (quote_ch != '\0') 24395892Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNTERMQUOTE), 24405892Sab196087 quote_ch); 24415892Sab196087 24425088Sab196087 gettok_state->gtok_last_token.tok_len = str - 24435088Sab196087 gettok_state->gtok_last_token.tok_str; 24445088Sab196087 gettok_state->gtok_null_seen = *look == '\0'; 24455088Sab196087 if (!gettok_state->gtok_null_seen) 24465088Sab196087 look++; 24475088Sab196087 *str = '\0'; 24485088Sab196087 gettok_state->gtok_cur_buf = look; 24495088Sab196087 24505892Sab196087 #ifdef DEBUG_GETTOK 24515892Sab196087 printf("GETTOK >"); 24525892Sab196087 elfedit_str_to_c_literal(gettok_state->gtok_last_token.tok_str, 24535892Sab196087 elfedit_write); 24545892Sab196087 printf("< \tlen(%d) offset(%d)\n", 24555088Sab196087 gettok_state->gtok_last_token.tok_len, 24565088Sab196087 gettok_state->gtok_last_token.tok_line_off); 24575088Sab196087 #endif 24585088Sab196087 24595088Sab196087 return (1); 24605088Sab196087 } 24615088Sab196087 24625088Sab196087 24635088Sab196087 /* 24645088Sab196087 * Tokenize the user command string, and return a pointer to the 24655088Sab196087 * TOK_STATE buffer maintained by this function. That buffer contains 24665088Sab196087 * the tokenized strings. 24675088Sab196087 * 24685088Sab196087 * entry: 24695088Sab196087 * user_cmd_str - String to tokenize 24705088Sab196087 * len - # of characters in user_cmd_str to examine. If 24715088Sab196087 * (len < 0), then the complete string is processed 24725088Sab196087 * stopping with the NULL termination. Otherwise, 24735088Sab196087 * processing stops after len characters, and any 24745088Sab196087 * remaining characters are ignored. 24755088Sab196087 * inc_null_final - If True, and if user_cmd_str has whitespace 24765088Sab196087 * at the end following the last non-null token, then 24775088Sab196087 * a final null token will be included. If False, null 24785088Sab196087 * tokens are ignored. 24795088Sab196087 * 24805088Sab196087 * note: 24815088Sab196087 * This routine returns pointers to internally allocated memory. 24825088Sab196087 * The caller must not alter anything contained in the TOK_STATE 24835088Sab196087 * buffer returned. Furthermore, the the contents of TOK_STATE 24845088Sab196087 * are only valid until the next call to tokenize_user_cmd(). 24855088Sab196087 */ 24865088Sab196087 static TOK_STATE * 24875088Sab196087 tokenize_user_cmd(const char *user_cmd_str, size_t len, int inc_null_final) 24885088Sab196087 { 24895088Sab196087 #define INITIAL_TOK_ALLOC 5 24905088Sab196087 24915088Sab196087 /* 24925088Sab196087 * As we parse the user command, we need temporary space to 24935088Sab196087 * hold the tokens. We do this by dynamically allocating a string 24945088Sab196087 * buffer and a token array, and doubling them as necessary. This 24955088Sab196087 * is a single threaded application, so static variables suffice. 24965088Sab196087 */ 24975088Sab196087 static STRBUF str; 24985088Sab196087 static TOK_STATE tokst; 24995088Sab196087 25005088Sab196087 GETTOK_STATE gettok_state; 25015088Sab196087 size_t n; 25025088Sab196087 25035088Sab196087 /* 25045088Sab196087 * Make a copy we can modify. If (len == 0), take the entire 25055088Sab196087 * string. Otherwise limit it to the specified length. 25065088Sab196087 */ 25075088Sab196087 tokst.tokst_cmd_len = strlen(user_cmd_str); 25085088Sab196087 if ((len > 0) && (len < tokst.tokst_cmd_len)) 25095088Sab196087 tokst.tokst_cmd_len = len; 25105088Sab196087 tokst.tokst_cmd_len++; /* Room for NULL termination */ 25115088Sab196087 strbuf_ensure_size(&str, tokst.tokst_cmd_len); 25125088Sab196087 (void) strlcpy(str.buf, user_cmd_str, tokst.tokst_cmd_len); 25135088Sab196087 25145088Sab196087 /* Trim off any newline character that might be present */ 25155088Sab196087 if ((tokst.tokst_cmd_len > 1) && 25165088Sab196087 (str.buf[tokst.tokst_cmd_len - 2] == '\n')) { 25175088Sab196087 tokst.tokst_cmd_len--; 25185088Sab196087 str.buf[tokst.tokst_cmd_len - 1] = '\0'; 25195088Sab196087 } 25205088Sab196087 25215088Sab196087 /* Tokenize the user command string into tok struct */ 25225088Sab196087 gettok_init(&gettok_state, str.buf, inc_null_final); 25235088Sab196087 tokst.tokst_str_size = 0; /* Space needed for token strings */ 25245088Sab196087 for (tokst.tokst_cnt = 0; gettok(&gettok_state) != 0; 25255088Sab196087 tokst.tokst_cnt++) { 25265088Sab196087 /* If we need more room, expand the token buffer */ 25275088Sab196087 if (tokst.tokst_cnt >= tokst.tokst_bufsize) { 25285088Sab196087 n = (tokst.tokst_bufsize == 0) ? 25295088Sab196087 INITIAL_TOK_ALLOC : (tokst.tokst_bufsize * 2); 25305088Sab196087 tokst.tokst_buf = elfedit_realloc( 25315088Sab196087 MSG_INTL(MSG_ALLOC_TOKBUF), tokst.tokst_buf, 25325088Sab196087 n * sizeof (*tokst.tokst_buf)); 25335088Sab196087 tokst.tokst_bufsize = n; 25345088Sab196087 } 25355088Sab196087 tokst.tokst_str_size += 25365088Sab196087 gettok_state.gtok_last_token.tok_len + 1; 25375088Sab196087 tokst.tokst_buf[tokst.tokst_cnt] = gettok_state.gtok_last_token; 25385088Sab196087 } 25395088Sab196087 /* fold the command token to lowercase */ 25405088Sab196087 if (tokst.tokst_cnt > 0) { 25415088Sab196087 char *s; 25425088Sab196087 25435088Sab196087 for (s = tokst.tokst_buf[0].tok_str; *s; s++) 25445088Sab196087 if (isupper(*s)) 25455088Sab196087 *s = tolower(*s); 25465088Sab196087 } 25475088Sab196087 25485088Sab196087 return (&tokst); 25495088Sab196087 25505088Sab196087 #undef INITIAL_TOK_ALLOC 25515088Sab196087 } 25525088Sab196087 25535088Sab196087 25545088Sab196087 /* 25555088Sab196087 * Parse the user command string, and put an entry for it at the end 25565088Sab196087 * of state.ucmd. 25575088Sab196087 */ 25585088Sab196087 static void 25595088Sab196087 parse_user_cmd(const char *user_cmd_str) 25605088Sab196087 { 25615088Sab196087 TOK_STATE *tokst; 25625088Sab196087 char *s; 25635088Sab196087 size_t n; 25645088Sab196087 size_t len; 25655088Sab196087 USER_CMD_T *ucmd; 25665088Sab196087 elfeditGC_module_t *mod; 25675088Sab196087 elfeditGC_cmd_t *cmd; 25685088Sab196087 25695088Sab196087 /* 25705088Sab196087 * Break it into tokens. If there are none, then it is 25715088Sab196087 * an empty command and is ignored. 25725088Sab196087 */ 25735088Sab196087 tokst = tokenize_user_cmd(user_cmd_str, -1, 0); 25745088Sab196087 if (tokst->tokst_cnt == 0) 25755088Sab196087 return; 25765088Sab196087 25775088Sab196087 /* Find the command. Won't return on error */ 25785088Sab196087 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 1, &mod); 25795088Sab196087 25805088Sab196087 /* 25815088Sab196087 * If there is no ELF file being edited, then only commands 25825088Sab196087 * from the sys: module are allowed. 25835088Sab196087 */ 25845088Sab196087 if ((state.file.present == 0) && 25855088Sab196087 (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) != 0)) 25865088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFILSYSONLY), 25875088Sab196087 mod->mod_name, cmd->cmd_name[0]); 25885088Sab196087 25895088Sab196087 25905088Sab196087 /* Allocate, fill in, and insert a USER_CMD_T block */ 25915088Sab196087 n = S_DROUND(sizeof (USER_CMD_T)); 25925088Sab196087 ucmd = elfedit_malloc(MSG_INTL(MSG_ALLOC_UCMD), 25935088Sab196087 n + (sizeof (char *) * (tokst->tokst_cnt - 1)) + 25945088Sab196087 tokst->tokst_cmd_len + tokst->tokst_str_size); 25955088Sab196087 ucmd->ucmd_next = NULL; 25965088Sab196087 ucmd->ucmd_argc = tokst->tokst_cnt - 1; 25975088Sab196087 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 25985088Sab196087 ucmd->ucmd_argv = (const char **)(n + (char *)ucmd); 25995088Sab196087 ucmd->ucmd_orig_str = (char *)(ucmd->ucmd_argv + ucmd->ucmd_argc); 26005088Sab196087 (void) strncpy(ucmd->ucmd_orig_str, user_cmd_str, tokst->tokst_cmd_len); 26015088Sab196087 ucmd->ucmd_mod = mod; 26025088Sab196087 ucmd->ucmd_cmd = cmd; 26035088Sab196087 ucmd->ucmd_ostyle_set = 0; 26045088Sab196087 s = ucmd->ucmd_orig_str + tokst->tokst_cmd_len; 26055088Sab196087 for (n = 1; n < tokst->tokst_cnt; n++) { 26065088Sab196087 len = tokst->tokst_buf[n].tok_len + 1; 26075088Sab196087 ucmd->ucmd_argv[n - 1] = s; 26085088Sab196087 (void) strncpy(s, tokst->tokst_buf[n].tok_str, len); 26095088Sab196087 s += len; 26105088Sab196087 } 26115088Sab196087 if (state.ucmd.list == NULL) { 26125088Sab196087 state.ucmd.list = state.ucmd.tail = ucmd; 26135088Sab196087 } else { 26145088Sab196087 state.ucmd.tail->ucmd_next = ucmd; 26155088Sab196087 state.ucmd.tail = ucmd; 26165088Sab196087 } 26175088Sab196087 state.ucmd.n++; 26185088Sab196087 } 26195088Sab196087 26205088Sab196087 26215088Sab196087 /* 26225088Sab196087 * Copy infile to a new file with the name given by outfile. 26235088Sab196087 */ 26245088Sab196087 static void 26255088Sab196087 create_outfile(const char *infile, const char *outfile) 26265088Sab196087 { 26275088Sab196087 pid_t pid; 26285088Sab196087 int statloc; 26295088Sab196087 struct stat statbuf; 26305088Sab196087 26315088Sab196087 26325088Sab196087 pid = fork(); 26335088Sab196087 switch (pid) { 26345088Sab196087 case -1: /* Unable to create process */ 26355088Sab196087 { 26365088Sab196087 int err = errno; 26375088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTFORK), 26385088Sab196087 strerror(err)); 26395088Sab196087 } 26405088Sab196087 /*NOTREACHED*/ 26415088Sab196087 return; 26425088Sab196087 26435088Sab196087 case 0: 26445088Sab196087 (void) execl(MSG_ORIG(MSG_STR_BINCP), 26455088Sab196087 MSG_ORIG(MSG_STR_BINCP), infile, outfile, NULL); 26465088Sab196087 /* 26475088Sab196087 * exec() only returns on error. This is the child process, 26485088Sab196087 * so we want to stay away from the usual error mechanism 26495088Sab196087 * and handle things directly. 26505088Sab196087 */ 26515088Sab196087 { 26525088Sab196087 int err = errno; 26535088Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_CNTEXEC), 26545088Sab196087 MSG_ORIG(MSG_STR_ELFEDIT), 26555088Sab196087 MSG_ORIG(MSG_STR_BINCP), strerror(err)); 26565088Sab196087 } 26575088Sab196087 exit(1); 26585088Sab196087 /*NOTREACHED*/ 26595088Sab196087 } 26605088Sab196087 26615088Sab196087 /* This is the parent: Wait for the child to terminate */ 26625088Sab196087 if (waitpid(pid, &statloc, 0) != pid) { 26635088Sab196087 int err = errno; 26645088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTWAIT), 26655088Sab196087 strerror(err)); 26665088Sab196087 } 26675088Sab196087 /* 26685088Sab196087 * If the child failed, then terminate the process. There is no 26695088Sab196087 * need for an error message, because the child will have taken 26705088Sab196087 * care of that. 26715088Sab196087 */ 26725088Sab196087 if (!WIFEXITED(statloc) || (WEXITSTATUS(statloc) != 0)) 26735088Sab196087 exit(1); 26745088Sab196087 26755088Sab196087 /* Make sure the copy allows user write access */ 26765088Sab196087 if (stat(outfile, &statbuf) == -1) { 26775088Sab196087 int err = errno; 26785088Sab196087 (void) unlink(outfile); 26795088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTSTAT), 26805088Sab196087 outfile, strerror(err)); 26815088Sab196087 } 26825088Sab196087 if ((statbuf.st_mode & S_IWUSR) == 0) { 26835088Sab196087 /* Only keep permission bits, and add user write */ 26845088Sab196087 statbuf.st_mode |= S_IWUSR; 26855088Sab196087 statbuf.st_mode &= 07777; /* Only keep the permission bits */ 26865088Sab196087 if (chmod(outfile, statbuf.st_mode) == -1) { 26875088Sab196087 int err = errno; 26885088Sab196087 (void) unlink(outfile); 26895088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTCHMOD), 26905088Sab196087 outfile, strerror(err)); 26915088Sab196087 } 26925088Sab196087 } 26935088Sab196087 } 26945088Sab196087 26955088Sab196087 /* 26965088Sab196087 * Given a module path string, determine how long the resulting path will 26975088Sab196087 * be when all % tokens have been expanded. 26985088Sab196087 * 26995088Sab196087 * entry: 27005088Sab196087 * path - Path for which expanded length is desired 27015088Sab196087 * origin_root - Root of $ORIGIN tree containing running elfedit program 27025088Sab196087 * 27035088Sab196087 * exit: 27045088Sab196087 * Returns the value strlen() will give for the expanded path. 27055088Sab196087 */ 27065088Sab196087 static size_t 27075088Sab196087 modpath_strlen(const char *path, const char *origin_root) 27085088Sab196087 { 27095088Sab196087 size_t len = 0; 27105088Sab196087 const char *s; 27115088Sab196087 27125088Sab196087 s = path; 27135088Sab196087 len = 0; 27145088Sab196087 for (s = path; *s != '\0'; s++) { 27155088Sab196087 if (*s == '%') { 27165088Sab196087 s++; 27175088Sab196087 switch (*s) { 27185088Sab196087 case 'i': /* ISA of running elfedit */ 27195088Sab196087 len += strlen(isa_i_str); 27205088Sab196087 break; 27215088Sab196087 case 'I': /* "" for 32-bit, same as %i for 64 */ 27225088Sab196087 len += strlen(isa_I_str); 27235088Sab196087 break; 27245088Sab196087 case 'o': /* Insert default path */ 27255088Sab196087 len += 27265088Sab196087 modpath_strlen(MSG_ORIG(MSG_STR_MODPATH), 27275088Sab196087 origin_root); 27285088Sab196087 break; 27295088Sab196087 case 'r': /* root of tree with running elfedit */ 27305088Sab196087 len += strlen(origin_root); 27315088Sab196087 break; 27325088Sab196087 27335088Sab196087 case '%': /* %% is reduced to just '%' */ 27345088Sab196087 len++; 27355088Sab196087 break; 27365088Sab196087 default: /* All other % codes are reserved */ 27375088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 27385088Sab196087 MSG_INTL(MSG_ERR_BADPATHCODE), *s); 27395088Sab196087 /*NOTREACHED*/ 27405088Sab196087 break; 27415088Sab196087 } 27425088Sab196087 } else { /* Non-% character passes straight through */ 27435088Sab196087 len++; 27445088Sab196087 } 27455088Sab196087 } 27465088Sab196087 27475088Sab196087 return (len); 27485088Sab196087 } 27495088Sab196087 27505088Sab196087 27515088Sab196087 /* 27525088Sab196087 * Given a module path string, and a buffer large enough to hold the results, 27535088Sab196087 * fill the buffer with the expanded path. 27545088Sab196087 * 27555088Sab196087 * entry: 27565088Sab196087 * path - Path for which expanded length is desired 27575088Sab196087 * origin_root - Root of tree containing running elfedit program 27585088Sab196087 * buf - Buffer to receive the result. buf must as large or larger 27595088Sab196087 * than the value given by modpath_strlen(). 27605088Sab196087 * 27615088Sab196087 * exit: 27625088Sab196087 * Returns pointer to location following the last character 27635088Sab196087 * written to buf. A NULL byte is written to that address. 27645088Sab196087 */ 27655088Sab196087 static char * 27665088Sab196087 modpath_expand(const char *path, const char *origin_root, char *buf) 27675088Sab196087 { 27685088Sab196087 size_t len; 27695088Sab196087 const char *cp_str; 27705088Sab196087 27715088Sab196087 for (; *path != '\0'; path++) { 27725088Sab196087 if (*path == '%') { 27735088Sab196087 path++; 27745088Sab196087 cp_str = NULL; 27755088Sab196087 switch (*path) { 27765088Sab196087 case 'i': /* ISA of running elfedit */ 27775088Sab196087 cp_str = isa_i_str; 27785088Sab196087 break; 27795088Sab196087 case 'I': /* "" for 32-bit, same as %i for 64 */ 27805088Sab196087 cp_str = isa_I_str; 27815088Sab196087 break; 27825088Sab196087 case 'o': /* Insert default path */ 27835088Sab196087 buf = modpath_expand(MSG_ORIG(MSG_STR_MODPATH), 27845088Sab196087 origin_root, buf); 27855088Sab196087 break; 27865088Sab196087 case 'r': 27875088Sab196087 cp_str = origin_root; 27885088Sab196087 break; 27895088Sab196087 case '%': /* %% is reduced to just '%' */ 27905088Sab196087 *buf++ = *path; 27915088Sab196087 break; 27925088Sab196087 default: /* All other % codes are reserved */ 27935088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 27945088Sab196087 MSG_INTL(MSG_ERR_BADPATHCODE), *path); 27955088Sab196087 /*NOTREACHED*/ 27965088Sab196087 break; 27975088Sab196087 } 27985088Sab196087 if ((cp_str != NULL) && ((len = strlen(cp_str)) > 0)) { 27995088Sab196087 bcopy(cp_str, buf, len); 28005088Sab196087 buf += len; 28015088Sab196087 } 28025088Sab196087 } else { /* Non-% character passes straight through */ 28035088Sab196087 *buf++ = *path; 28045088Sab196087 } 28055088Sab196087 } 28065088Sab196087 28075088Sab196087 *buf = '\0'; 28085088Sab196087 return (buf); 28095088Sab196087 } 28105088Sab196087 28115088Sab196087 28125088Sab196087 /* 28135088Sab196087 * Establish the module search path: state.modpath 28145088Sab196087 * 28155088Sab196087 * The path used comes from the following sources, taking the first 28165088Sab196087 * one that has a value, and ignoring any others: 28175088Sab196087 * 28185088Sab196087 * - ELFEDIT_PATH environment variable 28195088Sab196087 * - -L command line argument 28205088Sab196087 * - Default value 28215088Sab196087 * 28225088Sab196087 * entry: 28235088Sab196087 * path - NULL, or the value of the -L command line argument 28245088Sab196087 * 28255088Sab196087 * exit: 28265088Sab196087 * state.modpath has been filled in 28275088Sab196087 */ 28285088Sab196087 static void 28295088Sab196087 establish_modpath(const char *cmdline_path) 28305088Sab196087 { 28315088Sab196087 char origin_root[PATH_MAX + 1]; /* Where elfedit binary is */ 28325088Sab196087 const char *path; /* Initial path */ 28335088Sab196087 char *expath; /* Expanded path */ 28345088Sab196087 size_t len; 28355088Sab196087 char *src, *dst; 28365088Sab196087 28375088Sab196087 path = getenv(MSG_ORIG(MSG_STR_ENVVAR)); 28385088Sab196087 if (path == NULL) 28395088Sab196087 path = cmdline_path; 28405088Sab196087 if (path == NULL) 28415088Sab196087 path = MSG_ORIG(MSG_STR_MODPATH); 28425088Sab196087 28435088Sab196087 28445088Sab196087 /* 28455088Sab196087 * Root of tree containing running for running program. 32-bit elfedit 28465088Sab196087 * is installed in /usr/bin, and 64-bit elfedit is one level lower 28475088Sab196087 * in an ISA-specific subdirectory. So, we find the root by 28485088Sab196087 * getting the $ORGIN of the current running program, and trimming 28495088Sab196087 * off the last 2 (32-bit) or 3 (64-bit) directories. 28505088Sab196087 * 28515088Sab196087 * On a standard system, this will simply yield '/'. However, 28525088Sab196087 * doing it this way allows us to run elfedit from a proto area, 28535088Sab196087 * and pick up modules from the same proto area instead of those 28545088Sab196087 * installed on the system. 28555088Sab196087 */ 28565088Sab196087 if (dlinfo(RTLD_SELF, RTLD_DI_ORIGIN, &origin_root) == -1) 28575088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTGETORIGIN)); 28585088Sab196087 len = (sizeof (char *) == 8) ? 3 : 2; 28595088Sab196087 src = origin_root + strlen(origin_root); 28605088Sab196087 while ((src > origin_root) && (len > 0)) { 28615088Sab196087 if (*(src - 1) == '/') 28625088Sab196087 len--; 28635088Sab196087 src--; 28645088Sab196087 } 28655088Sab196087 *src = '\0'; 28665088Sab196087 28675088Sab196087 28685088Sab196087 /* 28695088Sab196087 * Calculate space needed to hold expanded path. Note that 28705088Sab196087 * this assumes that MSG_STR_MODPATH will never contain a '%o' 28715088Sab196087 * code, and so, the expansion is not recursive. The codes allowed 28725088Sab196087 * are: 28735088Sab196087 * %i - ISA of running elfedit (sparc, sparcv9, etc) 28745088Sab196087 * %I - 64-bit ISA: Same as %i for 64-bit versions of elfedit, 28755088Sab196087 * but yields empty string for 32-bit ISAs. 28765088Sab196087 * %o - The original (default) path. 28775088Sab196087 * %r - Root of tree holding elfedit program. 28785088Sab196087 * %% - A single % 28795088Sab196087 * 28805088Sab196087 * A % followed by anything else is an error. This allows us to 28815088Sab196087 * add new codes in the future without backward compatability issues. 28825088Sab196087 */ 28835088Sab196087 len = modpath_strlen(path, origin_root); 28845088Sab196087 28855088Sab196087 expath = elfedit_malloc(MSG_INTL(MSG_ALLOC_EXPATH), len + 1); 28865088Sab196087 (void) modpath_expand(path, origin_root, expath); 28875088Sab196087 28885088Sab196087 /* 28895088Sab196087 * Count path segments, eliminate extra '/', and replace ':' 28905088Sab196087 * with NULL. 28915088Sab196087 */ 28925088Sab196087 state.modpath.n = 1; 28935088Sab196087 for (src = dst = expath; *src; src++) { 28945088Sab196087 if (*src == '/') { 28955088Sab196087 switch (*(src + 1)) { 28965088Sab196087 case '/': 28975088Sab196087 case ':': 28985088Sab196087 case '\0': 28995088Sab196087 continue; 29005088Sab196087 } 29015088Sab196087 } 29025088Sab196087 if (*src == ':') { 29035088Sab196087 state.modpath.n++; 29045088Sab196087 *dst = '\0'; 29055088Sab196087 } else if (src != dst) { 29065088Sab196087 *dst = *src; 29075088Sab196087 } 29085088Sab196087 dst++; 29095088Sab196087 } 29105088Sab196087 if (src != dst) 29115088Sab196087 *dst = '\0'; 29125088Sab196087 29135088Sab196087 state.modpath.seg = elfedit_malloc(MSG_INTL(MSG_ALLOC_PATHARR), 29145088Sab196087 sizeof (state.modpath.seg[0]) * state.modpath.n); 29155088Sab196087 29165088Sab196087 src = expath; 29175088Sab196087 for (len = 0; len < state.modpath.n; len++) { 29185088Sab196087 if (*src == '\0') { 29195088Sab196087 state.modpath.seg[len] = MSG_ORIG(MSG_STR_DOT); 29205088Sab196087 src++; 29215088Sab196087 } else { 29225088Sab196087 state.modpath.seg[len] = src; 29235088Sab196087 src += strlen(src) + 1; 29245088Sab196087 } 29255088Sab196087 } 29265088Sab196087 } 29275088Sab196087 29285088Sab196087 /* 29295088Sab196087 * When interactive (reading commands from a tty), we catch 29305088Sab196087 * SIGINT in order to restart the outer command loop. 29315088Sab196087 */ 29325088Sab196087 /*ARGSUSED*/ 29335088Sab196087 static void 29345088Sab196087 sigint_handler(int sig, siginfo_t *sip, void *ucp) 29355088Sab196087 { 29365088Sab196087 /* Jump to the outer loop to resume */ 29375088Sab196087 if (state.msg_jbuf.active) { 29385088Sab196087 state.msg_jbuf.active = 0; 29395088Sab196087 siglongjmp(state.msg_jbuf.env, 1); 29405088Sab196087 } 29415088Sab196087 } 29425088Sab196087 29435088Sab196087 29445088Sab196087 static void 29455088Sab196087 usage(int full) 29465088Sab196087 { 29475088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_BRIEF)); 29485088Sab196087 if (full) { 29495088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL1)); 29505088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL2)); 29515088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL3)); 29525088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL4)); 29535088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL5)); 29545088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL6)); 29555088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL_LAST)); 29565088Sab196087 } 29575088Sab196087 elfedit_exit(2); 29585088Sab196087 } 29595088Sab196087 29605088Sab196087 29615088Sab196087 /* 29625088Sab196087 * In order to complete commands, we need to know about them, 29635088Sab196087 * which means that we need to force all the modules to be 29645088Sab196087 * loaded. This is a relatively expensive operation, so we use 29655088Sab196087 * this function, which avoids doing it more than once in a session. 29665088Sab196087 */ 29675088Sab196087 static void 29685088Sab196087 elfedit_cpl_load_modules(void) 29695088Sab196087 { 29705088Sab196087 static int loaded; 29715088Sab196087 29725088Sab196087 if (!loaded) { 29735088Sab196087 elfedit_load_modpath(); 29745088Sab196087 loaded = 1; /* Don't do it again */ 29755088Sab196087 } 29765088Sab196087 } 29775088Sab196087 29785088Sab196087 /* 29795088Sab196087 * Compare the token to the given string, and if they share a common 29805088Sab196087 * initial sequence, add the tail of string to the tecla command completion 29815088Sab196087 * buffer: 29825088Sab196087 * 29835088Sab196087 * entry: 29845088Sab196087 * cpldata - Current completion state 29855088Sab196087 * str - String to match against token 29865088Sab196087 * casefold - True to allow case insensitive completion, False 29875088Sab196087 * if case must match exactly. 29885088Sab196087 */ 29895088Sab196087 void 29905088Sab196087 elfedit_cpl_match(void *cpldata, const char *str, int casefold) 29915088Sab196087 { 29925088Sab196087 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata; 29935088Sab196087 const char *cont_suffix; 29945088Sab196087 const char *type_suffix; 29955088Sab196087 29965088Sab196087 /* 29975088Sab196087 * Reasons to return immediately: 29985088Sab196087 * - NULL strings have no completion value 29995088Sab196087 * - The string is shorter than the existing item being completed 30005088Sab196087 */ 30015088Sab196087 if ((str == NULL) || (*str == '\0') || 30025088Sab196087 ((cstate->ecpl_token_len != 0) && 30035088Sab196087 ((strlen(str) < cstate->ecpl_token_len)))) 30045088Sab196087 return; 30055088Sab196087 30065088Sab196087 /* If the string does not share the existing prefix, don't use it */ 30075088Sab196087 if (casefold) { 30085088Sab196087 if (strncasecmp(cstate->ecpl_token_str, str, 30095088Sab196087 cstate->ecpl_token_len) != 0) 30105088Sab196087 return; 30115088Sab196087 } else { 30125088Sab196087 if (strncmp(cstate->ecpl_token_str, str, 30135088Sab196087 cstate->ecpl_token_len) != 0) 30145088Sab196087 return; 30155088Sab196087 } 30165088Sab196087 30175088Sab196087 if (cstate->ecpl_add_mod_colon) { 30185088Sab196087 cont_suffix = type_suffix = MSG_ORIG(MSG_STR_COLON); 30195088Sab196087 } else { 30205088Sab196087 cont_suffix = MSG_ORIG(MSG_STR_SPACE); 30215088Sab196087 type_suffix = NULL; 30225088Sab196087 } 30235088Sab196087 (void) cpl_add_completion(cstate->ecpl_cpl, cstate->ecpl_line, 30245088Sab196087 cstate->ecpl_word_start, cstate->ecpl_word_end, 30255088Sab196087 str + cstate->ecpl_token_len, type_suffix, cont_suffix); 30265088Sab196087 30275088Sab196087 } 30285088Sab196087 30295088Sab196087 30305088Sab196087 /* 30316225Sab196087 * Convenience wrapper on elfedit_cpl_match(): Format an unsigned 30326225Sab196087 * 32-bit integer as a string and enter the result for command completion. 30336225Sab196087 */ 30346225Sab196087 void 30356225Sab196087 elfedit_cpl_ndx(void *cpldata, uint_t ndx) 30366225Sab196087 { 30376225Sab196087 Conv_inv_buf_t buf; 30386225Sab196087 30396225Sab196087 (void) snprintf(buf.buf, sizeof (buf.buf), 30406225Sab196087 MSG_ORIG(MSG_FMT_WORDVAL), ndx); 30416225Sab196087 elfedit_cpl_match(cpldata, buf.buf, 0); 30426225Sab196087 } 30436225Sab196087 30446225Sab196087 30456225Sab196087 /* 30465088Sab196087 * Compare the token to the names of the commands from the given module, 30475088Sab196087 * and if they share a common initial sequence, add the tail of string 30485088Sab196087 * to the tecla command completion buffer: 30495088Sab196087 * 30505088Sab196087 * entry: 30515088Sab196087 * tok_buf - Token user has entered 30525088Sab196087 * tok_len - strlen(tok_buf) 30535088Sab196087 * mod - Module definition from which commands should be matched 30545088Sab196087 * cpl, line, word_start, word_end, cont_suffix - As documented 30555088Sab196087 * for gl_get_line() and cpl_add_completion. 30565088Sab196087 */ 30575088Sab196087 static void 30585088Sab196087 match_module_cmds(ELFEDIT_CPL_STATE *cstate, elfeditGC_module_t *mod) 30595088Sab196087 { 30605088Sab196087 elfeditGC_cmd_t *cmd; 30615088Sab196087 const char **cmd_name; 30625088Sab196087 30635088Sab196087 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) 30645088Sab196087 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++) 30655088Sab196087 elfedit_cpl_match(cstate, *cmd_name, 1); 30665088Sab196087 } 30675088Sab196087 30685088Sab196087 30695088Sab196087 /* 30705088Sab196087 * Compare the token to the known module names, and add those that 30715088Sab196087 * match to the list of alternatives via elfedit_cpl_match(). 30725088Sab196087 * 30735088Sab196087 * entry: 30745088Sab196087 * load_all_modules - If True, causes all modules to be loaded 30755088Sab196087 * before processing is done. If False, only the modules 30765088Sab196087 * currently seen will be used. 30775088Sab196087 */ 30785088Sab196087 void 30795088Sab196087 elfedit_cpl_module(void *cpldata, int load_all_modules) 30805088Sab196087 { 30815088Sab196087 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata; 30825088Sab196087 MODLIST_T *modlist; 30835088Sab196087 30845088Sab196087 if (load_all_modules) 30855088Sab196087 elfedit_cpl_load_modules(); 30865088Sab196087 30875088Sab196087 for (modlist = state.modlist; modlist != NULL; 30885088Sab196087 modlist = modlist->ml_next) { 30895088Sab196087 elfedit_cpl_match(cstate, modlist->ml_mod->mod_name, 1); 30905088Sab196087 } 30915088Sab196087 } 30925088Sab196087 30935088Sab196087 30945088Sab196087 /* 30955088Sab196087 * Compare the token to all the known commands, and add those that 30965088Sab196087 * match to the list of alternatives. 30975088Sab196087 * 30985088Sab196087 * note: 30995088Sab196087 * This routine will force modules to be loaded as necessary to 31005088Sab196087 * obtain the names it needs to match. 31015088Sab196087 */ 31025088Sab196087 void 31035088Sab196087 elfedit_cpl_command(void *cpldata) 31045088Sab196087 { 31055088Sab196087 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata; 31065088Sab196087 ELFEDIT_CPL_STATE colon_state; 31075088Sab196087 const char *colon_pos; 31085088Sab196087 MODLIST_T *modlist; 31095088Sab196087 MODLIST_T *insdef; 31105088Sab196087 char buf[128]; 31115088Sab196087 31125088Sab196087 /* 31135088Sab196087 * Is there a colon in the command? If so, locate its offset within 31145088Sab196087 * the raw input line. 31155088Sab196087 */ 31165088Sab196087 for (colon_pos = cstate->ecpl_token_str; 31175088Sab196087 *colon_pos && (*colon_pos != ':'); colon_pos++) 31185088Sab196087 ; 31195088Sab196087 31205088Sab196087 /* 31215088Sab196087 * If no colon was seen, then we are completing a module name, 31225088Sab196087 * or one of the commands from 'sys:' 31235088Sab196087 */ 31245088Sab196087 if (*colon_pos == '\0') { 31255088Sab196087 /* 31265088Sab196087 * Setting cstate->add_mod_colon tells elfedit_cpl_match() 31275088Sab196087 * to add an implicit ':' to the names it matches. We use it 31285088Sab196087 * here so the user doesn't have to enter the ':' manually. 31295088Sab196087 * Hiding this in the opaque state instead of making it 31305088Sab196087 * an argument to that function gives us the ability to 31315088Sab196087 * change it later without breaking the published interface. 31325088Sab196087 */ 31335088Sab196087 cstate->ecpl_add_mod_colon = 1; 31345088Sab196087 elfedit_cpl_module(cpldata, 1); 31355088Sab196087 cstate->ecpl_add_mod_colon = 0; 31365088Sab196087 31375088Sab196087 /* Add bare (no sys: prefix) commands from the sys: module */ 31385088Sab196087 match_module_cmds(cstate, 31395088Sab196087 elfedit_load_module(MSG_ORIG(MSG_MOD_SYS), 1, 0)); 31405088Sab196087 31415088Sab196087 return; 31425088Sab196087 } 31435088Sab196087 31445088Sab196087 /* 31455088Sab196087 * A colon was seen, so we have a module name. Extract the name, 31465088Sab196087 * substituting 'sys' for the case where the given name is empty. 31475088Sab196087 */ 31485088Sab196087 if (colon_pos == 0) 31495088Sab196087 (void) strlcpy(buf, MSG_ORIG(MSG_MOD_SYS), sizeof (buf)); 31505088Sab196087 else 31515088Sab196087 elfedit_strnbcpy(buf, cstate->ecpl_token_str, 31525088Sab196087 colon_pos - cstate->ecpl_token_str, sizeof (buf)); 31535088Sab196087 31545088Sab196087 /* 31555088Sab196087 * Locate the module. If it isn't already loaded, make an explicit 31565088Sab196087 * attempt to load it and try again. If a module definition is 31575088Sab196087 * obtained, process the commands it supplies. 31585088Sab196087 */ 31595088Sab196087 modlist = module_loaded(buf, &insdef); 31605088Sab196087 if (modlist == NULL) { 31615088Sab196087 (void) elfedit_load_module(buf, 0, 0); 31625088Sab196087 modlist = module_loaded(buf, &insdef); 31635088Sab196087 } 31645088Sab196087 if (modlist != NULL) { 31655088Sab196087 /* 31665088Sab196087 * Make a copy of the cstate, and adjust the line and 31675088Sab196087 * token so that the new one starts just past the colon 31685088Sab196087 * character. We know that the colon exists because 31695088Sab196087 * of the preceeding test that found it. Therefore, we do 31705088Sab196087 * not need to test against running off the end of the 31715088Sab196087 * string here. 31725088Sab196087 */ 31735088Sab196087 colon_state = *cstate; 31745088Sab196087 while (colon_state.ecpl_line[colon_state.ecpl_word_start] != 31755088Sab196087 ':') 31765088Sab196087 colon_state.ecpl_word_start++; 31775088Sab196087 while (*colon_state.ecpl_token_str != ':') { 31785088Sab196087 colon_state.ecpl_token_str++; 31795088Sab196087 colon_state.ecpl_token_len--; 31805088Sab196087 } 31815088Sab196087 /* Skip past the ':' character */ 31825088Sab196087 colon_state.ecpl_word_start++; 31835088Sab196087 colon_state.ecpl_token_str++; 31845088Sab196087 colon_state.ecpl_token_len--; 31855088Sab196087 31865088Sab196087 match_module_cmds(&colon_state, modlist->ml_mod); 31875088Sab196087 } 31885088Sab196087 } 31895088Sab196087 31905088Sab196087 31915088Sab196087 /* 31925088Sab196087 * Command completion function for use with libtacla. 31935088Sab196087 */ 31945088Sab196087 /*ARGSUSED1*/ 31955088Sab196087 static int 31965088Sab196087 cmd_match_fcn(WordCompletion *cpl, void *data, const char *line, int word_end) 31975088Sab196087 { 31985088Sab196087 const char *argv[ELFEDIT_MAXCPLARGS]; 31995088Sab196087 ELFEDIT_CPL_STATE cstate; 32005088Sab196087 TOK_STATE *tokst; 32015088Sab196087 int ndx; 32025088Sab196087 int i; 32035088Sab196087 elfeditGC_module_t *mod; 32045088Sab196087 elfeditGC_cmd_t *cmd; 32055088Sab196087 int num_opt; 32065088Sab196087 int opt_term_seen; 32075088Sab196087 int skip_one; 32085088Sab196087 elfedit_cmd_optarg_t *optarg; 32095088Sab196087 elfedit_optarg_item_t item; 32105088Sab196087 int ostyle_ndx = -1; 32115088Sab196087 32125088Sab196087 /* 32135088Sab196087 * For debugging, enable the following block. It tells the tecla 32145088Sab196087 * library that the program using is going to write to stdout. 32155088Sab196087 * It will put the tty back into normal mode, and it will cause 32165088Sab196087 * tecla to redraw the current input line when it gets control back. 32175088Sab196087 */ 32185892Sab196087 #ifdef DEBUG_CMD_MATCH 32195088Sab196087 gl_normal_io(state.input.gl); 32205088Sab196087 #endif 32215088Sab196087 32225088Sab196087 /* 32235088Sab196087 * Tokenize the line up through word_end. The last token in 32245088Sab196087 * the list is the one requiring completion. 32255088Sab196087 */ 32265088Sab196087 tokst = tokenize_user_cmd(line, word_end, 1); 32275088Sab196087 if (tokst->tokst_cnt == 0) 32285088Sab196087 return (0); 32295088Sab196087 32305088Sab196087 /* Set up the cstate block, containing the completion state */ 32315088Sab196087 ndx = tokst->tokst_cnt - 1; /* Index of token to complete */ 32325088Sab196087 cstate.ecpl_cpl = cpl; 32335088Sab196087 cstate.ecpl_line = line; 32345088Sab196087 cstate.ecpl_word_start = tokst->tokst_buf[ndx].tok_line_off; 32355088Sab196087 cstate.ecpl_word_end = word_end; 32365088Sab196087 cstate.ecpl_add_mod_colon = 0; 32375088Sab196087 cstate.ecpl_token_str = tokst->tokst_buf[ndx].tok_str; 32385088Sab196087 cstate.ecpl_token_len = tokst->tokst_buf[ndx].tok_len; 32395088Sab196087 32405088Sab196087 /* 32415088Sab196087 * If there is only one token, then we are completing the 32425088Sab196087 * command itself. 32435088Sab196087 */ 32445088Sab196087 if (ndx == 0) { 32455088Sab196087 elfedit_cpl_command(&cstate); 32465088Sab196087 return (0); 32475088Sab196087 } 32485088Sab196087 32495088Sab196087 /* 32505088Sab196087 * There is more than one token. Use the first one to 32515088Sab196087 * locate the definition for the command. If we don't have 32525088Sab196087 * a definition for the command, then there's nothing more 32535088Sab196087 * we can do. 32545088Sab196087 */ 32555088Sab196087 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 0, &mod); 32565088Sab196087 if (cmd == NULL) 32575088Sab196087 return (0); 32585088Sab196087 32595088Sab196087 /* 32605088Sab196087 * Since we know the command, give them a quick usage message. 32615088Sab196087 * It may be that they just need a quick reminder about the form 32625088Sab196087 * of the command and the options. 32635088Sab196087 */ 32645088Sab196087 (void) gl_normal_io(state.input.gl); 32655088Sab196087 elfedit_printf(MSG_INTL(MSG_USAGE_CMD), 32665088Sab196087 elfedit_format_command_usage(mod, cmd, NULL, 0)); 32675088Sab196087 32685088Sab196087 32695088Sab196087 /* 32705088Sab196087 * We have a generous setting for ELFEDIT_MAXCPLARGS, so there 32715088Sab196087 * should always be plenty of room. If there's not room, we 32725088Sab196087 * can't proceed. 32735088Sab196087 */ 32745088Sab196087 if (ndx >= ELFEDIT_MAXCPLARGS) 32755088Sab196087 return (0); 32765088Sab196087 32775088Sab196087 /* 32785088Sab196087 * Put pointers to the tokens into argv, and determine how 32795088Sab196087 * many of the tokens are optional arguments. 32805088Sab196087 * 32815088Sab196087 * We consider the final optional argument to be the rightmost 32825088Sab196087 * argument that starts with a '-'. If a '--' is seen, then 32835088Sab196087 * we stop there, and any argument that follows is a plain argument 32845088Sab196087 * (even if it starts with '-'). 32855088Sab196087 * 32865088Sab196087 * We look for an inherited '-o' option, because we are willing 32875088Sab196087 * to supply command completion for these values. 32885088Sab196087 */ 32895088Sab196087 num_opt = 0; 32905088Sab196087 opt_term_seen = 0; 32915088Sab196087 skip_one = 0; 32925088Sab196087 for (i = 0; i < ndx; i++) { 32935088Sab196087 argv[i] = tokst->tokst_buf[i + 1].tok_str; 32945088Sab196087 if (opt_term_seen || skip_one) { 32955088Sab196087 skip_one = 0; 32965088Sab196087 continue; 32975088Sab196087 } 32985088Sab196087 skip_one = 0; 32995088Sab196087 ostyle_ndx = -1; 33005088Sab196087 if ((strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_MINUS)) == NULL) || 33015088Sab196087 (*argv[i] != '-')) { 33025088Sab196087 opt_term_seen = 1; 33035088Sab196087 continue; 33045088Sab196087 } 33055088Sab196087 num_opt = i + 1; 33065088Sab196087 /* 33075088Sab196087 * If it is a recognised ELFEDIT_CMDOA_F_VALUE option, 33085088Sab196087 * then the item following it is the associated value. 33095088Sab196087 * Check for this and skip the value. 33105088Sab196087 * 33115088Sab196087 * At the same time, look for STDOA_OPT_O inherited 33125088Sab196087 * options. We want to identify the index of any such 33135088Sab196087 * item. Although the option is simply "-o", we are willing 33145088Sab196087 * to treat any option that starts with "-o" as a potential 33155088Sab196087 * STDOA_OPT_O. This lets us to command completion for things 33165088Sab196087 * like "-onum", and is otherwise harmless, the only cost 33175088Sab196087 * being a few additional strcmps by the cpl code. 33185088Sab196087 */ 33195088Sab196087 if ((optarg = cmd->cmd_opt) == NULL) 33205088Sab196087 continue; 33215088Sab196087 while (optarg->oa_name != NULL) { 33225088Sab196087 int is_ostyle_optarg = 33235088Sab196087 (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) && 33245088Sab196087 (optarg->oa_name == ELFEDIT_STDOA_OPT_O); 33255088Sab196087 33265088Sab196087 elfedit_next_optarg(&optarg, &item); 33275088Sab196087 if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE) { 33285088Sab196087 if (is_ostyle_optarg && (strncmp(argv[i], 33295088Sab196087 MSG_ORIG(MSG_STR_MINUS_O), 2) == 0)) 33305088Sab196087 ostyle_ndx = i + 1; 33315088Sab196087 33325088Sab196087 if (strcmp(item.oai_name, argv[i]) == 0) { 33335088Sab196087 num_opt = i + 2; 33345088Sab196087 skip_one = 1; 33355088Sab196087 break; 33365088Sab196087 } 33375088Sab196087 /* 33385088Sab196087 * If it didn't match "-o" exactly, but it is 33395088Sab196087 * ostyle_ndx, then it is a potential combined 33405088Sab196087 * STDOA_OPT_O, as discussed above. It counts 33415088Sab196087 * as a single argument. 33425088Sab196087 */ 33435088Sab196087 if (ostyle_ndx == ndx) 33445088Sab196087 break; 33455088Sab196087 } 33465088Sab196087 } 33475088Sab196087 } 33485088Sab196087 33495892Sab196087 #ifdef DEBUG_CMD_MATCH 33505892Sab196087 (void) printf("NDX(%d) NUM_OPT(%d) ostyle_ndx(%d)\n", ndx, num_opt, 33515088Sab196087 ostyle_ndx); 33525088Sab196087 #endif 33535088Sab196087 33545088Sab196087 if (ostyle_ndx != -1) { 33555088Sab196087 /* 33565088Sab196087 * If ostyle_ndx is one less than ndx, and ndx is 33575088Sab196087 * the same as num_opt, then we have a definitive 33585088Sab196087 * STDOA_OPT_O inherited outstyle option. We supply 33595088Sab196087 * the value strings, and are done. 33605088Sab196087 */ 33615088Sab196087 if ((ostyle_ndx == (ndx - 1)) && (ndx == num_opt)) { 33625088Sab196087 elfedit_cpl_atoconst(&cstate, ELFEDIT_CONST_OUTSTYLE); 33635088Sab196087 return (0); 33645088Sab196087 } 33655088Sab196087 33665088Sab196087 /* 33675088Sab196087 * If ostyle is the same as ndx, then we have an option 33685088Sab196087 * staring with "-o" that may end up being a STDOA_OPT_O, 33695088Sab196087 * and we are still inside that token. In this case, we 33705088Sab196087 * supply completion strings that include the leading 33715088Sab196087 * "-o" followed by the values, without a space 33725088Sab196087 * (i.e. "-onum"). We then fall through, allowing any 33735088Sab196087 * other options starting with "-o" to be added 33745088Sab196087 * below. elfedit_cpl_match() will throw out the incorrect 33755088Sab196087 * options, so it is harmless to add these extra items in 33765088Sab196087 * the worst case, and useful otherwise. 33775088Sab196087 */ 33785088Sab196087 if (ostyle_ndx == ndx) 33795088Sab196087 elfedit_cpl_atoconst(&cstate, 33805088Sab196087 ELFEDIT_CONST_OUTSTYLE_MO); 33815088Sab196087 } 33825088Sab196087 33835088Sab196087 /* 33845088Sab196087 * If (ndx <= num_opt), then the token needing completion 33855088Sab196087 * is an option. If the leading '-' is there, then we should fill 33865088Sab196087 * in all of the option alternatives. If anything follows the '-' 33875088Sab196087 * though, we assume that the user has already figured out what 33885088Sab196087 * option to use, and we leave well enough alone. 33895088Sab196087 * 33905088Sab196087 * Note that we are intentionally ignoring a related case 33915088Sab196087 * where supplying option strings would be legal: In the case 33925088Sab196087 * where we are one past the last option (ndx == (num_opt + 1)), 33935088Sab196087 * and the current option is an empty string, the argument can 33945088Sab196087 * be either a plain argument or an option --- the user needs to 33955088Sab196087 * enter the next character before we can tell. It would be 33965088Sab196087 * OK to enter the option strings in this case. However, consider 33975088Sab196087 * what happens when the first plain argument to the command does 33985088Sab196087 * not provide any command completion (e.g. it is a plain integer). 33995088Sab196087 * In this case, tecla will see that all the alternatives start 34005088Sab196087 * with '-', and will insert a '-' into the input. If the user 34015088Sab196087 * intends the next argument to be plain, they will have to delete 34025088Sab196087 * this '-', which is annoying. Worse than that, they may be confused 34035088Sab196087 * by it, and think that the plain argument is not allowed there. 34045088Sab196087 * The best solution is to not supply option strings unless the 34055088Sab196087 * user first enters the '-'. 34065088Sab196087 */ 34075088Sab196087 if ((ndx <= num_opt) && (argv[ndx - 1][0] == '-')) { 34085088Sab196087 if ((optarg = cmd->cmd_opt) != NULL) { 34095088Sab196087 while (optarg->oa_name != NULL) { 34105088Sab196087 elfedit_next_optarg(&optarg, &item); 34115088Sab196087 elfedit_cpl_match(&cstate, item.oai_name, 1); 34125088Sab196087 } 34135088Sab196087 } 34145088Sab196087 return (0); 34155088Sab196087 } 34165088Sab196087 34175088Sab196087 /* 34185088Sab196087 * At this point we know that ndx and num_opt are not equal. 34195088Sab196087 * If num_opt is larger than ndx, then we have an ELFEDIT_CMDOA_F_VALUE 34205088Sab196087 * argument at the end, and the following value has not been entered. 34215088Sab196087 * 34225088Sab196087 * If ndx is greater than num_opt, it means that we are looking 34235088Sab196087 * at a plain argument (or in the case where (ndx == (num_opt + 1)), 34245088Sab196087 * a *potential* plain argument. 34255088Sab196087 * 34265088Sab196087 * If the command has a completion function registered, then we 34275088Sab196087 * hand off the remaining work to it. The cmd_cplfunc field is 34285088Sab196087 * the generic definition. We need to cast it to the type that matches 34295088Sab196087 * the proper ELFCLASS before calling it. 34305088Sab196087 */ 34315088Sab196087 if (state.elf.elfclass == ELFCLASS32) { 34325088Sab196087 elfedit32_cmdcpl_func_t *cmdcpl_func = 34335088Sab196087 (elfedit32_cmdcpl_func_t *)cmd->cmd_cplfunc; 34345088Sab196087 34355088Sab196087 if (cmdcpl_func != NULL) 34365088Sab196087 (* cmdcpl_func)(state.elf.obj_state.s32, 34375088Sab196087 &cstate, ndx, argv, num_opt); 34385088Sab196087 } else { 34395088Sab196087 elfedit64_cmdcpl_func_t *cmdcpl_func = 34405088Sab196087 (elfedit64_cmdcpl_func_t *)cmd->cmd_cplfunc; 34415088Sab196087 34425088Sab196087 if (cmdcpl_func != NULL) 34435088Sab196087 (* cmdcpl_func)(state.elf.obj_state.s64, 34445088Sab196087 &cstate, ndx, argv, num_opt); 34455088Sab196087 } 34465088Sab196087 34475088Sab196087 return (0); 34485088Sab196087 } 34495088Sab196087 34505088Sab196087 34515088Sab196087 /* 34525088Sab196087 * Read a line of input from stdin, and return pointer to it. 34535088Sab196087 * 34545088Sab196087 * This routine uses a private buffer, so the contents of the returned 34555088Sab196087 * string are only good until the next call. 34565088Sab196087 */ 34575088Sab196087 static const char * 34585088Sab196087 read_cmd(void) 34595088Sab196087 { 34605088Sab196087 char *s; 34615088Sab196087 34625088Sab196087 if (state.input.full_tty) { 34635088Sab196087 state.input.in_tecla = TRUE; 34645088Sab196087 s = gl_get_line(state.input.gl, 34655088Sab196087 MSG_ORIG(MSG_STR_PROMPT), NULL, -1); 34665088Sab196087 state.input.in_tecla = FALSE; 34675088Sab196087 /* 34685088Sab196087 * gl_get_line() returns NULL for EOF or for error. EOF is fine, 34695088Sab196087 * but we need to catch and report anything else. Since 34705088Sab196087 * reading from stdin is critical to our operation, an 34715088Sab196087 * error implies that we cannot recover and must exit. 34725088Sab196087 */ 34735088Sab196087 if ((s == NULL) && 34745088Sab196087 (gl_return_status(state.input.gl) == GLR_ERROR)) { 34755088Sab196087 elfedit_msg(ELFEDIT_MSG_FATAL, MSG_INTL(MSG_ERR_GLREAD), 34765088Sab196087 gl_error_message(state.input.gl, NULL, 0)); 34775088Sab196087 } 34785088Sab196087 } else { 34795088Sab196087 /* 34805088Sab196087 * This should be a dynamically sized buffer, but for now, 34815088Sab196087 * I'm going to take a simpler path. 34825088Sab196087 */ 34835088Sab196087 static char cmd_buf[ELFEDIT_MAXCMD + 1]; 34845088Sab196087 34855088Sab196087 s = fgets(cmd_buf, sizeof (cmd_buf), stdin); 34865088Sab196087 } 34875088Sab196087 34885088Sab196087 /* Return user string, or 'quit' on EOF */ 34895088Sab196087 return (s ? s : MSG_ORIG(MSG_SYS_CMD_QUIT)); 34905088Sab196087 } 34915088Sab196087 34925088Sab196087 int 34935088Sab196087 main(int argc, char **argv, char **envp) 34945088Sab196087 { 34955088Sab196087 /* 34965088Sab196087 * Note: This function can use setjmp()/longjmp() which does 34975088Sab196087 * not preserve the values of auto/register variables. Hence, 34985088Sab196087 * variables that need their values preserved across a jump must 34995088Sab196087 * be marked volatile, or must not be auto/register. 35005088Sab196087 * 35015088Sab196087 * Volatile can be messy, because it requires explictly casting 35025088Sab196087 * away the attribute when passing it to functions, or declaring 35035088Sab196087 * those functions with the attribute as well. In a single threaded 35045088Sab196087 * program like this one, an easier approach is to make things 35055088Sab196087 * static. That can be done here, or by putting things in the 35065088Sab196087 * 'state' structure. 35075088Sab196087 */ 35085088Sab196087 35095088Sab196087 int c, i; 35105088Sab196087 int num_batch = 0; 35115088Sab196087 char **batch_list = NULL; 35125088Sab196087 const char *modpath = NULL; 35135088Sab196087 35145088Sab196087 /* 35155088Sab196087 * Always have liblddb display unclipped section names. 35165088Sab196087 * This global is exported by liblddb, and declared in debug.h. 35175088Sab196087 */ 35185088Sab196087 dbg_desc->d_extra |= DBG_E_LONG; 35195088Sab196087 35205088Sab196087 opterr = 0; 35215088Sab196087 while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) { 35225088Sab196087 switch (c) { 35235088Sab196087 case 'a': 35245088Sab196087 state.flags |= ELFEDIT_F_AUTOPRINT; 35255088Sab196087 break; 35265088Sab196087 35275088Sab196087 case 'd': 35285088Sab196087 state.flags |= ELFEDIT_F_DEBUG; 35295088Sab196087 break; 35305088Sab196087 35315088Sab196087 case 'e': 35325088Sab196087 /* 35335088Sab196087 * Delay parsing the -e options until after the call to 35345088Sab196087 * conv_check_native() so that we won't bother loading 35355088Sab196087 * modules of the wrong class. 35365088Sab196087 */ 35375088Sab196087 if (batch_list == NULL) 35385088Sab196087 batch_list = elfedit_malloc( 35395088Sab196087 MSG_INTL(MSG_ALLOC_BATCHLST), 35405088Sab196087 sizeof (*batch_list) * (argc - 1)); 35415088Sab196087 batch_list[num_batch++] = optarg; 35425088Sab196087 break; 35435088Sab196087 35445088Sab196087 case 'L': 35455088Sab196087 modpath = optarg; 35465088Sab196087 break; 35475088Sab196087 35485088Sab196087 case 'o': 35495088Sab196087 if (elfedit_atooutstyle(optarg, &state.outstyle) == 0) 35505088Sab196087 usage(1); 35515088Sab196087 break; 35525088Sab196087 35535088Sab196087 case 'r': 35545088Sab196087 state.flags |= ELFEDIT_F_READONLY; 35555088Sab196087 break; 35565088Sab196087 35575088Sab196087 case '?': 35585088Sab196087 usage(1); 35595088Sab196087 } 35605088Sab196087 } 35615088Sab196087 35625088Sab196087 /* 35635088Sab196087 * We allow 0, 1, or 2 files: 35645088Sab196087 * 35655088Sab196087 * The no-file case is an extremely limited mode, in which the 35665088Sab196087 * only commands allowed to execute come from the sys: module. 35675088Sab196087 * This mode exists primarily to allow easy access to the help 35685088Sab196087 * facility. 35695088Sab196087 * 35705088Sab196087 * To get full access to elfedit's capablities, there must 35715088Sab196087 * be an input file. If this is not a readonly 35725088Sab196087 * session, then an optional second output file is allowed. 35735088Sab196087 * 35745088Sab196087 * In the case where two files are given and the session is 35755088Sab196087 * readonly, use a full usage message, because the simple 35765088Sab196087 * one isn't enough for the user to understand their error. 35775088Sab196087 * Otherwise, the simple usage message suffices. 35785088Sab196087 */ 35795088Sab196087 argc = argc - optind; 35805088Sab196087 if ((argc == 2) && (state.flags & ELFEDIT_F_READONLY)) 35815088Sab196087 usage(1); 35825088Sab196087 if (argc > 2) 35835088Sab196087 usage(0); 35845088Sab196087 35855088Sab196087 state.file.present = (argc != 0); 35865088Sab196087 35875088Sab196087 /* 35885088Sab196087 * If we have a file to edit, and unless told otherwise by the 35895088Sab196087 * caller, we try to run the 64-bit version of this program 35905088Sab196087 * when the system is capable of it. If that fails, then we 35915088Sab196087 * continue on with the currently running version. 35925088Sab196087 * 35935088Sab196087 * To force 32-bit execution on a 64-bit host, set the 35945088Sab196087 * LD_NOEXEC_64 environment variable to a non-empty value. 35955088Sab196087 * 35965088Sab196087 * There is no reason to bother with this if in "no file" mode. 35975088Sab196087 */ 35985088Sab196087 if (state.file.present != 0) 35995088Sab196087 (void) conv_check_native(argv, envp); 36005088Sab196087 36015088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_VERSION), 36025088Sab196087 (sizeof (char *) == 8) ? 64 : 32); 36035088Sab196087 36045088Sab196087 /* 36055088Sab196087 * Put a module definition for the builtin system module on the 36065088Sab196087 * module list. We know it starts out empty, so we do not have 36075088Sab196087 * to go through a more general insertion process than this. 36085088Sab196087 */ 36095088Sab196087 state.modlist = elfedit_sys_init(ELFEDIT_VER_CURRENT); 36105088Sab196087 36115088Sab196087 /* Establish the search path for loadable modules */ 36125088Sab196087 establish_modpath(modpath); 36135088Sab196087 36145088Sab196087 /* 36155088Sab196087 * Now that we are running the final version of this program, 36165088Sab196087 * deal with the input/output file(s). 36175088Sab196087 */ 36185088Sab196087 if (state.file.present == 0) { 36195088Sab196087 /* 36205088Sab196087 * This is arbitrary --- we simply need to be able to 36215088Sab196087 * load modules so that we can access their help strings 36225088Sab196087 * and command completion functions. Without a file, we 36235088Sab196087 * will refuse to call commands from any module other 36245088Sab196087 * than sys. Those commands have been written to be aware 36255088Sab196087 * of the case where there is no input file, and are 36265088Sab196087 * therefore safe to run. 36275088Sab196087 */ 36285088Sab196087 state.elf.elfclass = ELFCLASS32; 36295088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NOFILE)); 36305088Sab196087 36315088Sab196087 } else { 36325088Sab196087 state.file.infile = argv[optind]; 36335088Sab196087 if (argc == 1) { 36345088Sab196087 state.file.outfile = state.file.infile; 36355088Sab196087 if (state.flags & ELFEDIT_F_READONLY) 36365088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 36375088Sab196087 MSG_INTL(MSG_DEBUG_READONLY)); 36385088Sab196087 else 36395088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 36405088Sab196087 MSG_INTL(MSG_DEBUG_INPLACEWARN), 36415088Sab196087 state.file.infile); 36425088Sab196087 } else { 36435088Sab196087 state.file.outfile = argv[optind + 1]; 36445088Sab196087 create_outfile(state.file.infile, state.file.outfile); 36455088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 36465088Sab196087 MSG_INTL(MSG_DEBUG_CPFILE), 36475088Sab196087 state.file.infile, state.file.outfile); 36485088Sab196087 /* 36495088Sab196087 * We are editing a copy of the original file that we 36505088Sab196087 * just created. If we should exit before the edits are 36515088Sab196087 * updated, then we want to unlink this copy so that we 36525088Sab196087 * don't leave junk lying around. Once an update 36535088Sab196087 * succeeds however, we'll leave it in place even 36545088Sab196087 * if an error occurs afterwards. 36555088Sab196087 */ 36565088Sab196087 state.file.unlink_on_exit = 1; 36575088Sab196087 optind++; /* Edit copy instead of the original */ 36585088Sab196087 } 36595088Sab196087 36605088Sab196087 init_obj_state(state.file.outfile); 36615088Sab196087 } 36625088Sab196087 36635088Sab196087 36645088Sab196087 /* 36655088Sab196087 * Process commands. 36665088Sab196087 * 36675088Sab196087 * If any -e options were used, then do them and 36685088Sab196087 * immediately exit. On error, exit immediately without 36695088Sab196087 * updating the target ELF file. On success, the 'write' 36705088Sab196087 * and 'quit' commands are implicit in this mode. 36715088Sab196087 * 36725088Sab196087 * If no -e options are used, read commands from stdin. 36735088Sab196087 * quit must be explicitly used. Exit is implicit on EOF. 36745088Sab196087 * If stdin is a tty, then errors do not cause the editor 36755088Sab196087 * to terminate. Rather, the error message is printed, and the 36765088Sab196087 * user prompted to continue. 36775088Sab196087 */ 36785088Sab196087 if (batch_list != NULL) { /* -e was used */ 36795088Sab196087 /* Compile the commands */ 36805088Sab196087 for (i = 0; i < num_batch; i++) 36815088Sab196087 parse_user_cmd(batch_list[i]); 36825088Sab196087 free(batch_list); 36835088Sab196087 36845088Sab196087 /* 36855088Sab196087 * 'write' and 'quit' are implicit in this mode. 36865088Sab196087 * Add them as well. 36875088Sab196087 */ 36885088Sab196087 if ((state.flags & ELFEDIT_F_READONLY) == 0) 36895088Sab196087 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_WRITE)); 36905088Sab196087 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_QUIT)); 36915088Sab196087 36925088Sab196087 /* And run them. This won't return, thanks to the 'quit' */ 36935088Sab196087 dispatch_user_cmds(); 36945088Sab196087 } else { 36955088Sab196087 state.input.is_tty = isatty(fileno(stdin)); 36965088Sab196087 state.input.full_tty = state.input.is_tty && 36975088Sab196087 isatty(fileno(stdout)); 36985088Sab196087 36995088Sab196087 if (state.input.full_tty) { 37005088Sab196087 struct sigaction act; 37015088Sab196087 37025088Sab196087 act.sa_sigaction = sigint_handler; 37035088Sab196087 (void) sigemptyset(&act.sa_mask); 37045088Sab196087 act.sa_flags = 0; 37055088Sab196087 if (sigaction(SIGINT, &act, NULL) == -1) { 37065088Sab196087 int err = errno; 37075088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 37085088Sab196087 MSG_INTL(MSG_ERR_SIGACTION), strerror(err)); 37095088Sab196087 } 37105088Sab196087 /* 37115088Sab196087 * If pager process exits before we are done 37125088Sab196087 * writing, we can see SIGPIPE. Prevent it 37135088Sab196087 * from killing the process. 37145088Sab196087 */ 37155088Sab196087 (void) sigignore(SIGPIPE); 37165088Sab196087 37175088Sab196087 /* Open tecla handle for command line editing */ 37185088Sab196087 state.input.gl = new_GetLine(ELFEDIT_MAXCMD, 37195088Sab196087 ELFEDIT_MAXHIST); 37205088Sab196087 /* Register our command completion function */ 37215088Sab196087 (void) gl_customize_completion(state.input.gl, 37225088Sab196087 NULL, cmd_match_fcn); 37235088Sab196087 37245088Sab196087 /* 37255088Sab196087 * Make autoprint the default for interactive 37265088Sab196087 * sessions. 37275088Sab196087 */ 37285088Sab196087 state.flags |= ELFEDIT_F_AUTOPRINT; 37295088Sab196087 } 37305088Sab196087 for (;;) { 37315088Sab196087 /* 37325088Sab196087 * If this is an interactive session, then use 37335088Sab196087 * sigsetjmp()/siglongjmp() to recover from bad 37345088Sab196087 * commands and keep going. A non-0 return from 37355088Sab196087 * sigsetjmp() means that an error just occurred. 37365088Sab196087 * In that case, we simply restart this loop. 37375088Sab196087 */ 37385088Sab196087 if (state.input.is_tty) { 37395088Sab196087 if (sigsetjmp(state.msg_jbuf.env, 1) != 0) { 37405088Sab196087 if (state.input.full_tty) 37415088Sab196087 gl_abandon_line(state.input.gl); 37425088Sab196087 continue; 37435088Sab196087 } 37445088Sab196087 state.msg_jbuf.active = TRUE; 37455088Sab196087 } 37465088Sab196087 37475088Sab196087 /* 37485088Sab196087 * Force all output out before each command. 37495088Sab196087 * This is a no-OP when a tty is in use, but 37505088Sab196087 * in a pipeline, it ensures that the block 37515088Sab196087 * mode buffering doesn't delay output past 37525088Sab196087 * the completion of each command. 37535088Sab196087 * 37545088Sab196087 * If we didn't do this, the output would eventually 37555088Sab196087 * arrive at its destination, but the lag can be 37565088Sab196087 * annoying when you pipe the output into a tool 37575088Sab196087 * that displays the results in real time. 37585088Sab196087 */ 37595088Sab196087 (void) fflush(stdout); 37605088Sab196087 (void) fflush(stderr); 37615088Sab196087 37625088Sab196087 parse_user_cmd(read_cmd()); 37635088Sab196087 dispatch_user_cmds(); 37645088Sab196087 state.msg_jbuf.active = FALSE; 37655088Sab196087 } 37665088Sab196087 } 37675088Sab196087 37685088Sab196087 37695088Sab196087 /*NOTREACHED*/ 37705088Sab196087 return (0); 37715088Sab196087 } 3772