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