1 1618 rie /* 2 1618 rie * CDDL HEADER START 3 1618 rie * 4 1618 rie * The contents of this file are subject to the terms of the 5 1618 rie * Common Development and Distribution License (the "License"). 6 1618 rie * You may not use this file except in compliance with the License. 7 1618 rie * 8 1618 rie * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 1618 rie * or http://www.opensolaris.org/os/licensing. 10 1618 rie * See the License for the specific language governing permissions 11 1618 rie * and limitations under the License. 12 1618 rie * 13 1618 rie * When distributing Covered Code, include this CDDL HEADER in each 14 1618 rie * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 1618 rie * If applicable, add the following below this CDDL HEADER, with the 16 1618 rie * fields enclosed by brackets "[]" replaced with your own identifying 17 1618 rie * information: Portions Copyright [yyyy] [name of copyright owner] 18 1618 rie * 19 1618 rie * CDDL HEADER END 20 1618 rie */ 21 1618 rie 22 1618 rie /* 23 9085 Ali * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 1618 rie * Use is subject to license terms. 25 1618 rie */ 26 1618 rie 27 1618 rie /* 28 1618 rie * Dump an elf file. 29 1618 rie */ 30 1618 rie #include <sys/param.h> 31 1618 rie #include <fcntl.h> 32 1618 rie #include <stdio.h> 33 4168 ab196087 #include <stdlib.h> 34 4168 ab196087 #include <ctype.h> 35 1618 rie #include <libelf.h> 36 1618 rie #include <link.h> 37 1618 rie #include <stdarg.h> 38 1618 rie #include <unistd.h> 39 1618 rie #include <libgen.h> 40 1618 rie #include <libintl.h> 41 1618 rie #include <locale.h> 42 1618 rie #include <errno.h> 43 1618 rie #include <strings.h> 44 1618 rie #include <debug.h> 45 1618 rie #include <conv.h> 46 1618 rie #include <msg.h> 47 1618 rie #include <_elfdump.h> 48 5411 ab196087 #include <sys/elf_SPARC.h> 49 5411 ab196087 #include <sys/elf_amd64.h> 50 5411 ab196087 51 1618 rie 52 4063 ab196087 const Cache cache_init = {NULL, NULL, NULL, NULL, 0}; 53 4168 ab196087 54 4168 ab196087 55 4168 ab196087 56 5411 ab196087 /* 57 5411 ab196087 * The -I, -N, and -T options are called "match options", because 58 5411 ab196087 * they allow selecting the items to be displayed based on matching 59 5411 ab196087 * their index, name, or type. 60 5411 ab196087 * 61 5411 ab196087 * The ELF information to which -I, -N, or -T are applied in 62 5411 ab196087 * the current invocation is called the "match item". 63 5411 ab196087 */ 64 4168 ab196087 typedef enum { 65 5411 ab196087 MATCH_ITEM_PT, /* Program header (PT_) */ 66 5411 ab196087 MATCH_ITEM_SHT /* Section header (SHT_) */ 67 5411 ab196087 } match_item_t; 68 5411 ab196087 69 5411 ab196087 /* match_opt_t is used to note which match option was used */ 70 5411 ab196087 typedef enum { 71 5411 ab196087 MATCH_OPT_NAME, /* Record contains a name */ 72 5411 ab196087 MATCH_OPT_NDX, /* Record contains a single index */ 73 5411 ab196087 MATCH_OPT_RANGE, /* Record contains an index range */ 74 5411 ab196087 MATCH_OPT_TYPE, /* Record contains a type (shdr or phdr) */ 75 5411 ab196087 } match_opt_t; 76 4168 ab196087 77 4168 ab196087 typedef struct _match { 78 4168 ab196087 struct _match *next; /* Pointer to next item in list */ 79 5411 ab196087 match_opt_t opt_type; 80 4168 ab196087 union { 81 5411 ab196087 const char *name; /* MATCH_OPT_NAME */ 82 5411 ab196087 struct { /* MATCH_OPT_NDX and MATCH_OPT_RANGE */ 83 4168 ab196087 int start; 84 5411 ab196087 int end; /* Only for MATCH_OPT_RANGE */ 85 4168 ab196087 } ndx; 86 5411 ab196087 uint32_t type; /* MATCH_OPT_TYPE */ 87 4168 ab196087 } value; 88 5411 ab196087 } match_rec_t; 89 4168 ab196087 90 5411 ab196087 static struct { 91 5411 ab196087 match_item_t item_type; /* Type of item being matched */ 92 5411 ab196087 match_rec_t *list; /* Records for (-I, -N, -T) options */ 93 5411 ab196087 } match_state; 94 5411 ab196087 95 5411 ab196087 96 1618 rie 97 1618 rie const char * 98 1618 rie _elfdump_msg(Msg mid) 99 1618 rie { 100 1618 rie return (gettext(MSG_ORIG(mid))); 101 1618 rie } 102 1618 rie 103 1618 rie /* 104 1618 rie * Determine whether a symbol name should be demangled. 105 1618 rie */ 106 1618 rie const char * 107 1618 rie demangle(const char *name, uint_t flags) 108 1618 rie { 109 5411 ab196087 if (flags & FLG_CTL_DEMANGLE) 110 1618 rie return (Elf_demangle_name(name)); 111 1618 rie else 112 1618 rie return ((char *)name); 113 1618 rie } 114 1618 rie 115 1618 rie /* 116 1618 rie * Define our own standard error routine. 117 1618 rie */ 118 1618 rie void 119 1618 rie failure(const char *file, const char *func) 120 1618 rie { 121 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_ERR_FAILURE), 122 1618 rie file, func, elf_errmsg(elf_errno())); 123 1618 rie } 124 1618 rie 125 1618 rie /* 126 1618 rie * The full usage message 127 1618 rie */ 128 1618 rie static void 129 1618 rie detail_usage() 130 1618 rie { 131 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL1)); 132 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL2)); 133 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL3)); 134 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL4)); 135 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL5)); 136 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL6)); 137 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL7)); 138 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL8)); 139 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL9)); 140 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL10)); 141 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL11)); 142 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL12)); 143 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL13)); 144 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL14)); 145 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL15)); 146 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL16)); 147 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL17)); 148 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL18)); 149 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL19)); 150 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL20)); 151 3492 ab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL21)); 152 4168 ab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL22)); 153 4168 ab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL23)); 154 4665 ab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL24)); 155 5411 ab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL25)); 156 9273 Ali (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL26)); 157 6635 ab196087 } 158 6635 ab196087 159 6635 ab196087 /* 160 6635 ab196087 * Output a block of raw data as hex bytes. Each row is given 161 6635 ab196087 * the index of the first byte in the row. 162 6635 ab196087 * 163 6635 ab196087 * entry: 164 6635 ab196087 * data - Pointer to first byte of data to be displayed 165 6635 ab196087 * n - # of bytes of data 166 6635 ab196087 * prefix - String to be output before each line. Useful 167 6635 ab196087 * for indenting output. 168 6635 ab196087 * bytes_per_col - # of space separated bytes to output 169 6635 ab196087 * in each column. 170 6635 ab196087 * col_per_row - # of columns to output per row 171 6635 ab196087 * 172 6635 ab196087 * exit: 173 6635 ab196087 * The formatted data has been sent to stdout. Each row of output 174 6635 ab196087 * shows (bytes_per_col * col_per_row) bytes of data. 175 6635 ab196087 */ 176 6635 ab196087 void 177 9085 Ali dump_hex_bytes(const void *data, size_t n, int indent, 178 6635 ab196087 int bytes_per_col, int col_per_row) 179 6635 ab196087 { 180 9085 Ali const uchar_t *ldata = data; 181 6635 ab196087 int bytes_per_row = bytes_per_col * col_per_row; 182 6635 ab196087 int ndx, byte, word; 183 6635 ab196087 char string[128], *str = string; 184 6635 ab196087 char index[MAXNDXSIZE]; 185 6635 ab196087 int index_width; 186 6635 ab196087 int sp_prefix = 0; 187 6635 ab196087 188 6635 ab196087 189 6635 ab196087 /* 190 6635 ab196087 * Determine the width to use for the index string. We follow 191 6635 ab196087 * 8-byte tab rules, but don't use an actual \t character so 192 6635 ab196087 * that the output can be arbitrarily shifted without odd 193 6635 ab196087 * tab effects, and so that all the columns line up no matter 194 6635 ab196087 * how many lines of output are produced. 195 6635 ab196087 */ 196 6635 ab196087 ndx = n / bytes_per_row; 197 6635 ab196087 (void) snprintf(index, sizeof (index), 198 6635 ab196087 MSG_ORIG(MSG_FMT_INDEX2), EC_WORD(ndx)); 199 6635 ab196087 index_width = strlen(index); 200 6635 ab196087 index_width = S_ROUND(index_width, 8); 201 6635 ab196087 202 9085 Ali for (ndx = byte = word = 0; n > 0; n--, ldata++) { 203 6635 ab196087 while (sp_prefix-- > 0) 204 6635 ab196087 *str++ = ' '; 205 6635 ab196087 206 6635 ab196087 (void) snprintf(str, sizeof (string), 207 9085 Ali MSG_ORIG(MSG_HEXDUMP_TOK), (int)*ldata); 208 6635 ab196087 str += 2; 209 6635 ab196087 sp_prefix = 1; 210 6635 ab196087 211 6635 ab196087 if (++byte == bytes_per_col) { 212 6635 ab196087 sp_prefix += 2; 213 6635 ab196087 word++; 214 6635 ab196087 byte = 0; 215 6635 ab196087 } 216 6635 ab196087 if (word == col_per_row) { 217 6635 ab196087 *str = '\0'; 218 6635 ab196087 (void) snprintf(index, sizeof (index), 219 6635 ab196087 MSG_ORIG(MSG_FMT_INDEX2), EC_WORD(ndx)); 220 6635 ab196087 dbg_print(0, MSG_ORIG(MSG_HEXDUMP_ROW), 221 6635 ab196087 indent, MSG_ORIG(MSG_STR_EMPTY), 222 6635 ab196087 index_width, index, string); 223 6635 ab196087 sp_prefix = 0; 224 6635 ab196087 word = 0; 225 6635 ab196087 ndx += bytes_per_row; 226 6635 ab196087 str = string; 227 6635 ab196087 } 228 6635 ab196087 } 229 6635 ab196087 if (byte || word) { 230 6635 ab196087 *str = '\0'; /* */ 231 6635 ab196087 (void) snprintf(index, sizeof (index), 232 6635 ab196087 MSG_ORIG(MSG_FMT_INDEX2), EC_WORD(ndx)); 233 6635 ab196087 dbg_print(0, MSG_ORIG(MSG_HEXDUMP_ROW), indent, 234 6635 ab196087 MSG_ORIG(MSG_STR_EMPTY), index_width, index, string); 235 6635 ab196087 } 236 4168 ab196087 } 237 4168 ab196087 238 4168 ab196087 /* 239 4168 ab196087 * Convert the ASCII representation of an index, or index range, into 240 4168 ab196087 * binary form, and store it in rec: 241 4168 ab196087 * 242 4168 ab196087 * index: An positive or 0 valued integer 243 4168 ab196087 * range: Two indexes, separated by a ':' character, denoting 244 4168 ab196087 * a range of allowed values. If the second value is omitted, 245 4168 ab196087 * any values equal to or greater than the first will match. 246 4168 ab196087 * 247 4168 ab196087 * exit: 248 5411 ab196087 * On success, *rec is filled in with a MATCH_OPT_NDX or MATCH_OPT_RANGE 249 4168 ab196087 * value, and this function returns (1). On failure, the contents 250 4168 ab196087 * of *rec are undefined, and (0) is returned. 251 4168 ab196087 */ 252 4168 ab196087 int 253 5411 ab196087 process_index_opt(const char *str, match_rec_t *rec) 254 4168 ab196087 { 255 4168 ab196087 #define SKIP_BLANK for (; *str && isspace(*str); str++) 256 4168 ab196087 257 4168 ab196087 char *endptr; 258 4168 ab196087 259 4168 ab196087 rec->value.ndx.start = strtol(str, &endptr, 10); 260 4168 ab196087 /* Value must use some of the input, and be 0 or positive */ 261 4168 ab196087 if ((str == endptr) || (rec->value.ndx.start < 0)) 262 4168 ab196087 return (0); 263 4168 ab196087 str = endptr; 264 4168 ab196087 265 4168 ab196087 SKIP_BLANK; 266 4168 ab196087 if (*str != ':') { 267 5411 ab196087 rec->opt_type = MATCH_OPT_NDX; 268 4168 ab196087 } else { 269 4168 ab196087 str++; /* Skip the ':' */ 270 5411 ab196087 rec->opt_type = MATCH_OPT_RANGE; 271 4168 ab196087 SKIP_BLANK; 272 4168 ab196087 if (*str == '\0') { 273 4168 ab196087 rec->value.ndx.end = -1; /* Indicates "to end" */ 274 4168 ab196087 } else { 275 4168 ab196087 rec->value.ndx.end = strtol(str, &endptr, 10); 276 4168 ab196087 if ((str == endptr) || (rec->value.ndx.end < 0)) 277 4168 ab196087 return (0); 278 4168 ab196087 str = endptr; 279 4168 ab196087 SKIP_BLANK; 280 4168 ab196087 } 281 4168 ab196087 } 282 4168 ab196087 283 4168 ab196087 /* Syntax error if anything is left over */ 284 4168 ab196087 if (*str != '\0') 285 4168 ab196087 return (0); 286 4168 ab196087 287 4168 ab196087 return (1); 288 4168 ab196087 289 4168 ab196087 #undef SKIP_BLANK 290 4168 ab196087 } 291 4168 ab196087 292 4168 ab196087 /* 293 9273 Ali * Convert a string containing a specific type of ELF constant, or an ASCII 294 9273 Ali * representation of a number, to an integer. Strings starting with '0' 295 5411 ab196087 * are taken to be octal, those staring with '0x' are hex, and all 296 5411 ab196087 * others are decimal. 297 5411 ab196087 * 298 5411 ab196087 * entry: 299 5411 ab196087 * str - String to be converted 300 9273 Ali * ctype - Constant type 301 5411 ab196087 * v - Address of variable to receive resulting value. 302 5411 ab196087 * 303 5411 ab196087 * exit: 304 5411 ab196087 * On success, returns True (1) and *v is set to the value. 305 5411 ab196087 * On failure, returns False (0) and *v is undefined. 306 5411 ab196087 */ 307 9273 Ali typedef enum { 308 9273 Ali ATOUI_PT, 309 9273 Ali ATOUI_SHT, 310 9273 Ali ATOUI_OSABI 311 9273 Ali } atoui_type_t; 312 9273 Ali 313 5411 ab196087 static int 314 9273 Ali atoui(const char *str, atoui_type_t type, uint32_t *v) 315 5411 ab196087 { 316 9273 Ali conv_strtol_uvalue_t uvalue; 317 9273 Ali char *endptr; 318 5411 ab196087 319 9273 Ali if (conv_iter_strtol_init(str, &uvalue) != 0) { 320 9273 Ali switch (type) { 321 9273 Ali case ATOUI_PT: 322 9273 Ali if (conv_iter_phdr_type(CONV_OSABI_ALL, CONV_FMT_ALT_CF, 323 9273 Ali conv_iter_strtol, &uvalue) == CONV_ITER_DONE) 324 9273 Ali break; 325 9273 Ali (void) conv_iter_phdr_type(CONV_OSABI_ALL, 326 9273 Ali CONV_FMT_ALT_NF, conv_iter_strtol, &uvalue); 327 9273 Ali break; 328 9273 Ali case ATOUI_SHT: 329 9273 Ali if (conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL, 330 9273 Ali CONV_FMT_ALT_CF, conv_iter_strtol, &uvalue) == 331 9273 Ali CONV_ITER_DONE) 332 9273 Ali break; 333 9273 Ali (void) conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL, 334 9273 Ali CONV_FMT_ALT_NF, conv_iter_strtol, &uvalue); 335 9273 Ali break; 336 9273 Ali case ATOUI_OSABI: 337 9273 Ali if (conv_iter_ehdr_osabi(CONV_FMT_ALT_CF, 338 9273 Ali conv_iter_strtol, &uvalue) == CONV_ITER_DONE) 339 9273 Ali break; 340 9273 Ali (void) conv_iter_ehdr_osabi(CONV_FMT_ALT_NF, 341 9273 Ali conv_iter_strtol, &uvalue); 342 9273 Ali break; 343 9273 Ali } 344 9273 Ali if (uvalue.csl_found) { 345 9273 Ali *v = uvalue.csl_value; 346 9273 Ali return (1); 347 9273 Ali } 348 9273 Ali } 349 5411 ab196087 350 5411 ab196087 *v = strtoull(str, &endptr, 0); 351 5411 ab196087 352 5411 ab196087 /* If the left over part contains anything but whitespace, fail */ 353 5411 ab196087 for (; *endptr; endptr++) 354 5411 ab196087 if (!isspace(*endptr)) 355 5411 ab196087 return (0); 356 5411 ab196087 return (1); 357 5411 ab196087 } 358 5411 ab196087 359 5411 ab196087 /* 360 5411 ab196087 * Called after getopt() processing is finished if there is a non-empty 361 5411 ab196087 * match list. Prepares the matching code for use. 362 5411 ab196087 * 363 5411 ab196087 * exit: 364 5411 ab196087 * Returns True (1) if no errors are encountered. Writes an 365 5411 ab196087 * error string to stderr and returns False (0) otherwise. 366 5411 ab196087 */ 367 5411 ab196087 static int 368 5411 ab196087 match_prepare(char *argv0, uint_t flags) 369 5411 ab196087 { 370 5411 ab196087 match_rec_t *list; 371 5411 ab196087 const char *str; 372 5411 ab196087 int minus_p = (flags & FLG_SHOW_PHDR) != 0; 373 9273 Ali atoui_type_t atoui_type; 374 5411 ab196087 375 5411 ab196087 /* 376 5411 ab196087 * Flag ambiguous attempt to use match option with both -p and 377 5411 ab196087 * and one or more section SHOW options. In this case, we 378 5411 ab196087 * can't tell what type of item we're supposed to match against. 379 5411 ab196087 */ 380 5411 ab196087 if (minus_p && (flags & FLG_MASK_SHOW_SHDR)) { 381 5411 ab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_AMBIG_MATCH), 382 5411 ab196087 basename(argv0)); 383 5411 ab196087 return (0); 384 5411 ab196087 } 385 5411 ab196087 386 5411 ab196087 /* Set the match type, based on the presence of the -p option */ 387 5411 ab196087 if (minus_p) { 388 5411 ab196087 match_state.item_type = MATCH_ITEM_PT; 389 9273 Ali atoui_type = ATOUI_PT; 390 5411 ab196087 } else { 391 5411 ab196087 match_state.item_type = MATCH_ITEM_SHT; 392 9273 Ali atoui_type = ATOUI_SHT; 393 5411 ab196087 } 394 5411 ab196087 395 5411 ab196087 /* 396 5411 ab196087 * Scan match list and perform any necessary fixups: 397 5411 ab196087 * 398 5411 ab196087 * MATCH_OPT_NAME: If -p is specified, convert MATCH_OPT_NAME (-N) 399 5411 ab196087 * requests into MATCH_OPT_TYPE (-T). 400 5411 ab196087 * 401 5411 ab196087 * MATCH_OPT_TYPE: Now that we know item type we are matching 402 5411 ab196087 * against, we can convert the string saved in the name 403 5411 ab196087 * field during getopt() processing into an integer and 404 5411 ab196087 * write it into the type field. 405 5411 ab196087 */ 406 5411 ab196087 for (list = match_state.list; list; list = list->next) { 407 5411 ab196087 if ((list->opt_type == MATCH_OPT_NAME) && minus_p) 408 5411 ab196087 list->opt_type = MATCH_OPT_TYPE; 409 5411 ab196087 410 5411 ab196087 if (list->opt_type != MATCH_OPT_TYPE) 411 5411 ab196087 continue; 412 5411 ab196087 413 5411 ab196087 str = list->value.name; 414 9273 Ali if (atoui(str, atoui_type, &list->value.type) == 0) { 415 5411 ab196087 const char *fmt = minus_p ? 416 5411 ab196087 MSG_INTL(MSG_ERR_BAD_T_PT) : 417 5411 ab196087 MSG_INTL(MSG_ERR_BAD_T_SHT); 418 5411 ab196087 419 5411 ab196087 (void) fprintf(stderr, fmt, basename(argv0), str); 420 5411 ab196087 return (0); 421 5411 ab196087 } 422 5411 ab196087 } 423 5411 ab196087 424 5411 ab196087 return (1); 425 5411 ab196087 } 426 5411 ab196087 427 5411 ab196087 428 5411 ab196087 /* 429 4168 ab196087 * Returns True (1) if the item with the given name or index should 430 4168 ab196087 * be displayed, and False (0) if it should not be. 431 4168 ab196087 * 432 4168 ab196087 * entry: 433 5411 ab196087 * match_flags - Bitmask specifying matching options, as described 434 5411 ab196087 * in _elfdump.h. 435 5411 ab196087 * name - If MATCH_F_NAME flag is set, name of item under 436 5411 ab196087 * consideration. Otherwise ignored. 437 4168 ab196087 * should not be considered. 438 5411 ab196087 * ndx - If MATCH_F_NDX flag is set, index of item under consideration. 439 5411 ab196087 * type - If MATCH_F_TYPE is set, type of item under consideration. 440 5411 ab196087 * If MATCH_F_PHDR is set, this would be a program 441 5411 ab196087 * header type (PT_). Otherwise, a section header type (SHT_). 442 4168 ab196087 * 443 4168 ab196087 * exit: 444 4168 ab196087 * True will be returned if the given name/index matches those given 445 5411 ab196087 * by one of the (-I, -N -T) command line options, or if no such option 446 5411 ab196087 * was used in the command invocation and MATCH_F_STRICT is not 447 5411 ab196087 * set. 448 4168 ab196087 */ 449 4168 ab196087 int 450 5411 ab196087 match(match_flags_t match_flags, const char *name, uint_t ndx, uint_t type) 451 4168 ab196087 { 452 5411 ab196087 match_item_t item_type = (match_flags & MATCH_F_PHDR) ? 453 5411 ab196087 MATCH_ITEM_PT : MATCH_ITEM_SHT; 454 5411 ab196087 match_rec_t *list; 455 4168 ab196087 456 5411 ab196087 /* 457 5411 ab196087 * If there is no match list, then we use the MATCH_F_STRICT 458 5411 ab196087 * flag to decide what to return. In the strict case, we return 459 5411 ab196087 * False (0), in the normal case, True (1). 460 5411 ab196087 */ 461 5411 ab196087 if (match_state.list == NULL) 462 5411 ab196087 return ((match_flags & MATCH_F_STRICT) == 0); 463 5411 ab196087 464 5411 ab196087 /* 465 5411 ab196087 * If item being checked is not the current match type, 466 5411 ab196087 * then allow it. 467 5411 ab196087 */ 468 5411 ab196087 if (item_type != match_state.item_type) 469 4168 ab196087 return (1); 470 4168 ab196087 471 4168 ab196087 /* Run through the match records and check for a hit */ 472 5411 ab196087 for (list = match_state.list; list; list = list->next) { 473 5411 ab196087 switch (list->opt_type) { 474 5411 ab196087 case MATCH_OPT_NAME: 475 5411 ab196087 if (((match_flags & MATCH_F_NAME) == 0) || 476 5411 ab196087 (name == NULL)) 477 5411 ab196087 break; 478 5411 ab196087 if (strcmp(list->value.name, name) == 0) 479 4168 ab196087 return (1); 480 4168 ab196087 break; 481 5411 ab196087 case MATCH_OPT_NDX: 482 5411 ab196087 if ((match_flags & MATCH_F_NDX) && 483 5411 ab196087 (ndx == list->value.ndx.start)) 484 4168 ab196087 return (1); 485 4168 ab196087 break; 486 5411 ab196087 case MATCH_OPT_RANGE: 487 4168 ab196087 /* 488 4168 ab196087 * A range end value less than 0 means that any value 489 4168 ab196087 * above the start is acceptible. 490 4168 ab196087 */ 491 5411 ab196087 if ((match_flags & MATCH_F_NDX) && 492 5411 ab196087 (ndx >= list->value.ndx.start) && 493 4168 ab196087 ((list->value.ndx.end < 0) || 494 4168 ab196087 (ndx <= list->value.ndx.end))) 495 5411 ab196087 return (1); 496 5411 ab196087 break; 497 5411 ab196087 498 5411 ab196087 case MATCH_OPT_TYPE: 499 5411 ab196087 if ((match_flags & MATCH_F_TYPE) && 500 5411 ab196087 (type == list->value.type)) 501 4168 ab196087 return (1); 502 4168 ab196087 break; 503 4168 ab196087 } 504 4168 ab196087 } 505 4168 ab196087 506 4168 ab196087 /* Nothing matched */ 507 4168 ab196087 return (0); 508 4168 ab196087 } 509 4168 ab196087 510 4168 ab196087 /* 511 5411 ab196087 * Add an entry to match_state.list for use by match(). This routine is for 512 5411 ab196087 * use during getopt() processing. It should not be called once 513 5411 ab196087 * match_prepare() has been called. 514 4168 ab196087 * 515 4168 ab196087 * Return True (1) for success. On failure, an error is written 516 4168 ab196087 * to stderr, and False (0) is returned. 517 4168 ab196087 */ 518 4168 ab196087 static int 519 5411 ab196087 add_match_record(char *argv0, match_rec_t *data) 520 4168 ab196087 { 521 5411 ab196087 match_rec_t *rec; 522 5411 ab196087 match_rec_t *list; 523 4168 ab196087 524 4168 ab196087 if ((rec = malloc(sizeof (*rec))) == NULL) { 525 4168 ab196087 int err = errno; 526 4168 ab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_MALLOC), 527 4168 ab196087 basename(argv0), strerror(err)); 528 4168 ab196087 return (0); 529 4168 ab196087 } 530 4168 ab196087 531 4168 ab196087 *rec = *data; 532 4168 ab196087 533 5411 ab196087 /* Insert at end of match_state.list */ 534 5411 ab196087 if (match_state.list == NULL) { 535 5411 ab196087 match_state.list = rec; 536 4168 ab196087 } else { 537 5411 ab196087 for (list = match_state.list; list->next != NULL; 538 5411 ab196087 list = list->next) 539 4168 ab196087 ; 540 4168 ab196087 list->next = rec; 541 4168 ab196087 } 542 4168 ab196087 543 4168 ab196087 rec->next = NULL; 544 4168 ab196087 return (1); 545 1618 rie } 546 1618 rie 547 5411 ab196087 static int 548 5411 ab196087 decide(const char *file, int fd, Elf *elf, uint_t flags, 549 9273 Ali const char *wname, int wfd, uchar_t osabi) 550 1618 rie { 551 5411 ab196087 int r; 552 5411 ab196087 553 1618 rie if (gelf_getclass(elf) == ELFCLASS64) 554 9273 Ali r = regular64(file, fd, elf, flags, wname, wfd, osabi); 555 1618 rie else 556 9273 Ali r = regular32(file, fd, elf, flags, wname, wfd, osabi); 557 5411 ab196087 558 5411 ab196087 return (r); 559 1618 rie } 560 1618 rie 561 5411 ab196087 static int 562 5411 ab196087 archive(const char *file, int fd, Elf *elf, uint_t flags, 563 9273 Ali const char *wname, int wfd, uchar_t osabi) 564 1618 rie { 565 1618 rie Elf_Cmd cmd = ELF_C_READ; 566 1618 rie Elf_Arhdr *arhdr; 567 9085 Ali Elf *_elf = NULL; 568 1618 rie size_t ptr; 569 9085 Ali Elf_Arsym *arsym = NULL; 570 1618 rie 571 1618 rie /* 572 3492 ab196087 * Determine if the archive symbol table itself is required. 573 1618 rie */ 574 5411 ab196087 if ((flags & FLG_SHOW_SYMBOLS) && 575 5411 ab196087 match(MATCH_F_NAME, MSG_ORIG(MSG_ELF_ARSYM), 0, 0)) { 576 1618 rie /* 577 1618 rie * Get the archive symbol table. 578 1618 rie */ 579 1618 rie if (((arsym = elf_getarsym(elf, &ptr)) == 0) && elf_errno()) { 580 1618 rie /* 581 1618 rie * The arsym could be 0 even though there was no error. 582 1618 rie * Print the error message only when there was 583 1618 rie * real error from elf_getarsym(). 584 1618 rie */ 585 1618 rie failure(file, MSG_ORIG(MSG_ELF_GETARSYM)); 586 5411 ab196087 return (0); 587 1618 rie } 588 1618 rie } 589 1618 rie 590 1618 rie /* 591 1618 rie * Print the archive symbol table only when the archive symbol 592 1618 rie * table exists and it was requested to print. 593 1618 rie */ 594 1618 rie if (arsym) { 595 1618 rie size_t cnt; 596 1618 rie char index[MAXNDXSIZE]; 597 1618 rie size_t offset = 0, _offset = 0; 598 1618 rie 599 1618 rie /* 600 1618 rie * Print out all the symbol entries. 601 1618 rie */ 602 1618 rie dbg_print(0, MSG_INTL(MSG_ARCHIVE_SYMTAB)); 603 1618 rie dbg_print(0, MSG_INTL(MSG_ARCHIVE_FIELDS)); 604 1618 rie 605 1618 rie for (cnt = 0; cnt < ptr; cnt++, arsym++) { 606 1618 rie /* 607 1618 rie * For each object obtain an elf descriptor so that we 608 1618 rie * can establish the members name. Note, we have had 609 1618 rie * archives where the archive header has not been 610 1618 rie * obtainable so be lenient with errors. 611 1618 rie */ 612 1618 rie if ((offset == 0) || ((arsym->as_off != 0) && 613 1618 rie (arsym->as_off != _offset))) { 614 1618 rie 615 1618 rie if (_elf) 616 1618 rie (void) elf_end(_elf); 617 1618 rie 618 1618 rie if (elf_rand(elf, arsym->as_off) != 619 1618 rie arsym->as_off) { 620 1618 rie failure(file, MSG_ORIG(MSG_ELF_RAND)); 621 9085 Ali arhdr = NULL; 622 1618 rie } else if ((_elf = elf_begin(fd, 623 1618 rie ELF_C_READ, elf)) == 0) { 624 1618 rie failure(file, MSG_ORIG(MSG_ELF_BEGIN)); 625 9085 Ali arhdr = NULL; 626 1618 rie } else if ((arhdr = elf_getarhdr(_elf)) == 0) { 627 1618 rie failure(file, 628 1618 rie MSG_ORIG(MSG_ELF_GETARHDR)); 629 9085 Ali arhdr = NULL; 630 1618 rie } 631 1618 rie 632 1618 rie _offset = arsym->as_off; 633 1618 rie if (offset == 0) 634 1618 rie offset = _offset; 635 1618 rie } 636 1618 rie 637 1618 rie (void) snprintf(index, MAXNDXSIZE, 638 1618 rie MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(cnt)); 639 1618 rie if (arsym->as_off) 640 1618 rie dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM1), index, 641 1618 rie /* LINTED */ 642 1618 rie (int)arsym->as_off, arhdr ? arhdr->ar_name : 643 1618 rie MSG_INTL(MSG_STR_UNKNOWN), (arsym->as_name ? 644 1618 rie demangle(arsym->as_name, flags) : 645 1618 rie MSG_INTL(MSG_STR_NULL))); 646 1618 rie else 647 1618 rie dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM2), index, 648 1618 rie /* LINTED */ 649 1618 rie (int)arsym->as_off); 650 1618 rie } 651 1618 rie 652 1618 rie if (_elf) 653 1618 rie (void) elf_end(_elf); 654 1618 rie 655 1618 rie /* 656 1618 rie * If we only need the archive symbol table return. 657 1618 rie */ 658 5411 ab196087 if ((flags & FLG_SHOW_SYMBOLS) && 659 5411 ab196087 match(MATCH_F_STRICT | MATCH_F_NAME, 660 5411 ab196087 MSG_ORIG(MSG_ELF_ARSYM), -1, -1)) 661 5411 ab196087 return (0); 662 1618 rie 663 1618 rie /* 664 1618 rie * Reset elf descriptor in preparation for processing each 665 1618 rie * member. 666 1618 rie */ 667 1618 rie if (offset) 668 1618 rie (void) elf_rand(elf, offset); 669 1618 rie } 670 1618 rie 671 1618 rie /* 672 1618 rie * Process each object within the archive. 673 1618 rie */ 674 1618 rie while ((_elf = elf_begin(fd, cmd, elf)) != NULL) { 675 1618 rie char name[MAXPATHLEN]; 676 1618 rie 677 1618 rie if ((arhdr = elf_getarhdr(_elf)) == NULL) { 678 1618 rie failure(file, MSG_ORIG(MSG_ELF_GETARHDR)); 679 5411 ab196087 return (0); 680 1618 rie } 681 1618 rie if (*arhdr->ar_name != '/') { 682 1618 rie (void) snprintf(name, MAXPATHLEN, 683 1618 rie MSG_ORIG(MSG_FMT_ARNAME), file, arhdr->ar_name); 684 1618 rie dbg_print(0, MSG_ORIG(MSG_FMT_NLSTR), name); 685 1618 rie 686 1618 rie switch (elf_kind(_elf)) { 687 1618 rie case ELF_K_AR: 688 5411 ab196087 if (archive(name, fd, _elf, flags, 689 9273 Ali wname, wfd, osabi) == 1) 690 5411 ab196087 return (1); 691 1618 rie break; 692 1618 rie case ELF_K_ELF: 693 5411 ab196087 if (decide(name, fd, _elf, flags, 694 9273 Ali wname, wfd, osabi) == 1) 695 5411 ab196087 return (1); 696 1618 rie break; 697 1618 rie default: 698 1618 rie (void) fprintf(stderr, 699 1618 rie MSG_INTL(MSG_ERR_BADFILE), name); 700 1618 rie break; 701 1618 rie } 702 1618 rie } 703 1618 rie 704 1618 rie cmd = elf_next(_elf); 705 1618 rie (void) elf_end(_elf); 706 1618 rie } 707 5411 ab196087 708 5411 ab196087 return (0); 709 1618 rie } 710 1618 rie 711 1618 rie int 712 1618 rie main(int argc, char **argv, char **envp) 713 1618 rie { 714 1618 rie Elf *elf; 715 1618 rie int var, fd, wfd = 0; 716 5411 ab196087 char *wname = NULL; 717 1618 rie uint_t flags = 0; 718 5411 ab196087 match_rec_t match_data; 719 5411 ab196087 int ret; 720 9273 Ali uchar_t osabi; 721 1618 rie 722 1618 rie /* 723 1618 rie * If we're on a 64-bit kernel, try to exec a full 64-bit version of 724 1618 rie * the binary. If successful, conv_check_native() won't return. 725 1618 rie */ 726 2647 rie (void) conv_check_native(argv, envp); 727 1618 rie 728 1618 rie /* 729 1618 rie * Establish locale. 730 1618 rie */ 731 1618 rie (void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY)); 732 1618 rie (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS)); 733 1618 rie 734 1618 rie (void) setvbuf(stdout, NULL, _IOLBF, 0); 735 1618 rie (void) setvbuf(stderr, NULL, _IOLBF, 0); 736 1618 rie 737 1618 rie opterr = 0; 738 1618 rie while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) { 739 1618 rie switch (var) { 740 1618 rie case 'C': 741 5411 ab196087 flags |= FLG_CTL_DEMANGLE; 742 1618 rie break; 743 1618 rie case 'c': 744 5411 ab196087 flags |= FLG_SHOW_SHDR; 745 1618 rie break; 746 1618 rie case 'd': 747 5411 ab196087 flags |= FLG_SHOW_DYNAMIC; 748 1618 rie break; 749 1618 rie case 'e': 750 5411 ab196087 flags |= FLG_SHOW_EHDR; 751 1618 rie break; 752 1618 rie case 'G': 753 5411 ab196087 flags |= FLG_SHOW_GOT; 754 1618 rie break; 755 1618 rie case 'g': 756 5411 ab196087 flags |= FLG_SHOW_GROUP; 757 1618 rie break; 758 1618 rie case 'H': 759 5411 ab196087 flags |= FLG_SHOW_CAP; 760 1618 rie break; 761 1618 rie case 'h': 762 5411 ab196087 flags |= FLG_SHOW_HASH; 763 1618 rie break; 764 4168 ab196087 case 'I': 765 5411 ab196087 if (!process_index_opt(optarg, &match_data)) 766 5411 ab196087 goto usage_brief; 767 4168 ab196087 if (!add_match_record(argv[0], &match_data)) 768 4168 ab196087 return (1); 769 5411 ab196087 flags |= FLG_CTL_MATCH; 770 4168 ab196087 break; 771 1618 rie case 'i': 772 5411 ab196087 flags |= FLG_SHOW_INTERP; 773 1618 rie break; 774 1618 rie case 'k': 775 5411 ab196087 flags |= FLG_CALC_CHECKSUM; 776 1618 rie break; 777 1618 rie case 'l': 778 5411 ab196087 flags |= FLG_CTL_LONGNAME; 779 1618 rie break; 780 1618 rie case 'm': 781 5411 ab196087 flags |= FLG_SHOW_MOVE; 782 1618 rie break; 783 1618 rie case 'N': 784 5411 ab196087 match_data.opt_type = MATCH_OPT_NAME; 785 4168 ab196087 match_data.value.name = optarg; 786 4168 ab196087 if (!add_match_record(argv[0], &match_data)) 787 4168 ab196087 return (1); 788 5411 ab196087 flags |= FLG_CTL_MATCH; 789 1618 rie break; 790 1618 rie case 'n': 791 5411 ab196087 flags |= FLG_SHOW_NOTE; 792 9273 Ali break; 793 9273 Ali case 'O': 794 9273 Ali { 795 9273 Ali uint32_t val; 796 9273 Ali 797 9273 Ali /* 798 9273 Ali * osabi is a uchar_t in the ELF header. 799 9273 Ali * Don't accept any value that exceeds 800 9273 Ali * that range. 801 9273 Ali */ 802 9273 Ali if ((atoui(optarg, ATOUI_OSABI, &val) == 0) || 803 9273 Ali (val > 255)) { 804 9273 Ali (void) fprintf(stderr, 805 9273 Ali MSG_INTL(MSG_ERR_BAD_T_OSABI), 806 9273 Ali basename(argv[0]), optarg); 807 9273 Ali return (1); 808 9273 Ali } 809 9273 Ali osabi = val; 810 9273 Ali } 811 9273 Ali flags |= FLG_CTL_OSABI; 812 1618 rie break; 813 4665 ab196087 case 'P': 814 5411 ab196087 flags |= FLG_CTL_FAKESHDR; 815 4665 ab196087 break; 816 1618 rie case 'p': 817 5411 ab196087 flags |= FLG_SHOW_PHDR; 818 1618 rie break; 819 1618 rie case 'r': 820 5411 ab196087 flags |= FLG_SHOW_RELOC; 821 1618 rie break; 822 3492 ab196087 case 'S': 823 5411 ab196087 flags |= FLG_SHOW_SORT; 824 3492 ab196087 break; 825 1618 rie case 's': 826 5411 ab196087 flags |= FLG_SHOW_SYMBOLS; 827 5411 ab196087 break; 828 5411 ab196087 case 'T': 829 5411 ab196087 /* 830 5411 ab196087 * We can't evaluate the value yet, because 831 5411 ab196087 * we need to know if -p is used or not in 832 5411 ab196087 * order to tell if we're seeing section header 833 5411 ab196087 * or program header types. So, we save the 834 5411 ab196087 * string in the name field, and then convert 835 5411 ab196087 * it to a type integer in a following pass. 836 5411 ab196087 */ 837 5411 ab196087 match_data.opt_type = MATCH_OPT_TYPE; 838 5411 ab196087 match_data.value.name = optarg; 839 5411 ab196087 if (!add_match_record(argv[0], &match_data)) 840 5411 ab196087 return (1); 841 5411 ab196087 flags |= FLG_CTL_MATCH; 842 1618 rie break; 843 1618 rie case 'u': 844 5411 ab196087 flags |= FLG_SHOW_UNWIND; 845 1618 rie break; 846 1618 rie case 'v': 847 5411 ab196087 flags |= FLG_SHOW_VERSIONS; 848 1618 rie break; 849 1618 rie case 'w': 850 1618 rie wname = optarg; 851 1618 rie break; 852 1618 rie case 'y': 853 5411 ab196087 flags |= FLG_SHOW_SYMINFO; 854 1618 rie break; 855 1618 rie case '?': 856 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), 857 1618 rie basename(argv[0])); 858 1618 rie detail_usage(); 859 1618 rie return (1); 860 1618 rie default: 861 1618 rie break; 862 1618 rie } 863 1618 rie } 864 1618 rie 865 5411 ab196087 /* -p and -w are mutually exclusive. -w only works with sections */ 866 5411 ab196087 if (((flags & FLG_SHOW_PHDR) != 0) && (wname != NULL)) 867 5411 ab196087 goto usage_brief; 868 5411 ab196087 869 5411 ab196087 /* If a match argument is present, prepare the match state */ 870 5411 ab196087 if ((match_state.list != NULL) && (match_prepare(argv[0], flags) == 0)) 871 5411 ab196087 return (1); 872 5411 ab196087 873 1618 rie /* 874 5411 ab196087 * Decide what to do if no options specifying something to 875 5411 ab196087 * show or do are present. 876 5411 ab196087 * 877 5411 ab196087 * If there is no -w and no match options, then we will set all 878 5411 ab196087 * the show flags, causing a full display of everything in the 879 5411 ab196087 * file that we know how to handle. 880 5411 ab196087 * 881 5411 ab196087 * Otherwise, if there is no match list, we generate a usage 882 5411 ab196087 * error and quit. 883 5411 ab196087 * 884 5411 ab196087 * In the case where there is a match list, we go ahead and call 885 5411 ab196087 * regular() anyway, leaving it to decide what to do. If -w is 886 5411 ab196087 * present, regular() will use the match list to handle it. 887 5411 ab196087 * In addition, in the absence of explicit show/calc flags, regular() 888 5411 ab196087 * will compare the section headers to the match list and use 889 5411 ab196087 * that to generate the FLG_ bits that will display the information 890 5411 ab196087 * specified by the match list. 891 1618 rie */ 892 5411 ab196087 if ((flags & ~FLG_MASK_CTL) == 0) { 893 5411 ab196087 if (!wname && (match_state.list == NULL)) 894 5411 ab196087 flags |= FLG_MASK_SHOW; 895 5411 ab196087 else if (match_state.list == NULL) 896 5411 ab196087 goto usage_brief; 897 1618 rie } 898 1618 rie 899 5411 ab196087 /* There needs to be at least 1 filename left following the options */ 900 5411 ab196087 if ((var = argc - optind) == 0) 901 5411 ab196087 goto usage_brief; 902 1618 rie 903 1618 rie /* 904 1618 rie * If the -l/-C option is specified, set up the liblddbg.so. 905 1618 rie */ 906 5411 ab196087 if (flags & FLG_CTL_LONGNAME) 907 1618 rie dbg_desc->d_extra |= DBG_E_LONG; 908 5411 ab196087 if (flags & FLG_CTL_DEMANGLE) 909 1618 rie dbg_desc->d_extra |= DBG_E_DEMANGLE; 910 1618 rie 911 1618 rie /* 912 1618 rie * If the -w option has indicated an output file open it. It's 913 1618 rie * arguable whether this option has much use when multiple files are 914 1618 rie * being processed. 915 5411 ab196087 * 916 5411 ab196087 * If wname is non-NULL, we know that -p was not specified, due 917 5411 ab196087 * to the test above. 918 1618 rie */ 919 1618 rie if (wname) { 920 1618 rie if ((wfd = open(wname, (O_RDWR | O_CREAT | O_TRUNC), 921 1618 rie 0666)) < 0) { 922 1618 rie int err = errno; 923 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN), 924 1618 rie wname, strerror(err)); 925 5411 ab196087 return (1); 926 1618 rie } 927 1618 rie } 928 1618 rie 929 1618 rie /* 930 5411 ab196087 * Open the input file, initialize the elf interface, and 931 5411 ab196087 * process it. 932 1618 rie */ 933 5411 ab196087 ret = 0; 934 5411 ab196087 for (; (optind < argc) && (ret == 0); optind++) { 935 1618 rie const char *file = argv[optind]; 936 1618 rie 937 1618 rie if ((fd = open(argv[optind], O_RDONLY)) == -1) { 938 1618 rie int err = errno; 939 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN), 940 1618 rie file, strerror(err)); 941 1618 rie continue; 942 1618 rie } 943 1618 rie (void) elf_version(EV_CURRENT); 944 1618 rie if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 945 1618 rie failure(file, MSG_ORIG(MSG_ELF_BEGIN)); 946 1618 rie (void) close(fd); 947 1618 rie continue; 948 1618 rie } 949 1618 rie 950 1618 rie if (var > 1) 951 1618 rie dbg_print(0, MSG_ORIG(MSG_FMT_NLSTRNL), file); 952 1618 rie 953 1618 rie switch (elf_kind(elf)) { 954 1618 rie case ELF_K_AR: 955 9273 Ali ret = archive(file, fd, elf, flags, wname, wfd, osabi); 956 1618 rie break; 957 1618 rie case ELF_K_ELF: 958 9273 Ali ret = decide(file, fd, elf, flags, wname, wfd, osabi); 959 1618 rie break; 960 1618 rie default: 961 1618 rie (void) fprintf(stderr, MSG_INTL(MSG_ERR_BADFILE), file); 962 1618 rie break; 963 1618 rie } 964 1618 rie 965 1618 rie (void) close(fd); 966 1618 rie (void) elf_end(elf); 967 1618 rie } 968 1618 rie 969 1618 rie if (wfd) 970 1618 rie (void) close(wfd); 971 5411 ab196087 return (ret); 972 5411 ab196087 973 5411 ab196087 usage_brief: 974 5411 ab196087 /* Control comes here for a simple usage message and exit */ 975 5411 ab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), 976 5411 ab196087 basename(argv[0])); 977 5411 ab196087 return (1); 978 5411 ab196087 979 1618 rie } 980