11618Srie /* 21618Srie * CDDL HEADER START 31618Srie * 41618Srie * The contents of this file are subject to the terms of the 51618Srie * Common Development and Distribution License (the "License"). 61618Srie * You may not use this file except in compliance with the License. 71618Srie * 81618Srie * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91618Srie * or http://www.opensolaris.org/os/licensing. 101618Srie * See the License for the specific language governing permissions 111618Srie * and limitations under the License. 121618Srie * 131618Srie * When distributing Covered Code, include this CDDL HEADER in each 141618Srie * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151618Srie * If applicable, add the following below this CDDL HEADER, with the 161618Srie * fields enclosed by brackets "[]" replaced with your own identifying 171618Srie * information: Portions Copyright [yyyy] [name of copyright owner] 181618Srie * 191618Srie * CDDL HEADER END 201618Srie */ 211618Srie 221618Srie /* 239085SAli.Bahrami@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 241618Srie * Use is subject to license terms. 251618Srie */ 261618Srie 271618Srie /* 281618Srie * Dump an elf file. 291618Srie */ 301618Srie #include <sys/param.h> 311618Srie #include <fcntl.h> 321618Srie #include <stdio.h> 334168Sab196087 #include <stdlib.h> 344168Sab196087 #include <ctype.h> 351618Srie #include <libelf.h> 361618Srie #include <link.h> 371618Srie #include <stdarg.h> 381618Srie #include <unistd.h> 391618Srie #include <libgen.h> 401618Srie #include <libintl.h> 411618Srie #include <locale.h> 421618Srie #include <errno.h> 431618Srie #include <strings.h> 441618Srie #include <debug.h> 451618Srie #include <conv.h> 461618Srie #include <msg.h> 471618Srie #include <_elfdump.h> 485411Sab196087 #include <sys/elf_SPARC.h> 495411Sab196087 #include <sys/elf_amd64.h> 505411Sab196087 511618Srie 524063Sab196087 const Cache cache_init = {NULL, NULL, NULL, NULL, 0}; 531618Srie 544168Sab196087 554168Sab196087 565411Sab196087 /* 575411Sab196087 * The -I, -N, and -T options are called "match options", because 585411Sab196087 * they allow selecting the items to be displayed based on matching 595411Sab196087 * their index, name, or type. 605411Sab196087 * 615411Sab196087 * The ELF information to which -I, -N, or -T are applied in 625411Sab196087 * the current invocation is called the "match item". 635411Sab196087 */ 644168Sab196087 typedef enum { 655411Sab196087 MATCH_ITEM_PT, /* Program header (PT_) */ 665411Sab196087 MATCH_ITEM_SHT /* Section header (SHT_) */ 675411Sab196087 } match_item_t; 685411Sab196087 695411Sab196087 /* match_opt_t is used to note which match option was used */ 705411Sab196087 typedef enum { 715411Sab196087 MATCH_OPT_NAME, /* Record contains a name */ 725411Sab196087 MATCH_OPT_NDX, /* Record contains a single index */ 735411Sab196087 MATCH_OPT_RANGE, /* Record contains an index range */ 745411Sab196087 MATCH_OPT_TYPE, /* Record contains a type (shdr or phdr) */ 755411Sab196087 } match_opt_t; 764168Sab196087 774168Sab196087 typedef struct _match { 784168Sab196087 struct _match *next; /* Pointer to next item in list */ 795411Sab196087 match_opt_t opt_type; 804168Sab196087 union { 815411Sab196087 const char *name; /* MATCH_OPT_NAME */ 825411Sab196087 struct { /* MATCH_OPT_NDX and MATCH_OPT_RANGE */ 834168Sab196087 int start; 845411Sab196087 int end; /* Only for MATCH_OPT_RANGE */ 854168Sab196087 } ndx; 865411Sab196087 uint32_t type; /* MATCH_OPT_TYPE */ 874168Sab196087 } value; 885411Sab196087 } match_rec_t; 895411Sab196087 905411Sab196087 static struct { 915411Sab196087 match_item_t item_type; /* Type of item being matched */ 925411Sab196087 match_rec_t *list; /* Records for (-I, -N, -T) options */ 935411Sab196087 } match_state; 945411Sab196087 955411Sab196087 965411Sab196087 971618Srie const char * 981618Srie _elfdump_msg(Msg mid) 991618Srie { 1001618Srie return (gettext(MSG_ORIG(mid))); 1011618Srie } 1021618Srie 1031618Srie /* 1041618Srie * Determine whether a symbol name should be demangled. 1051618Srie */ 1061618Srie const char * 1071618Srie demangle(const char *name, uint_t flags) 1081618Srie { 1095411Sab196087 if (flags & FLG_CTL_DEMANGLE) 1101618Srie return (Elf_demangle_name(name)); 1111618Srie else 1121618Srie return ((char *)name); 1131618Srie } 1141618Srie 1151618Srie /* 1161618Srie * Define our own standard error routine. 1171618Srie */ 1181618Srie void 1191618Srie failure(const char *file, const char *func) 1201618Srie { 1211618Srie (void) fprintf(stderr, MSG_INTL(MSG_ERR_FAILURE), 1221618Srie file, func, elf_errmsg(elf_errno())); 1231618Srie } 1241618Srie 1251618Srie /* 1261618Srie * The full usage message 1271618Srie */ 1281618Srie static void 1291618Srie detail_usage() 1301618Srie { 1311618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL1)); 1321618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL2)); 1331618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL3)); 1341618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL4)); 1351618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL5)); 1361618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL6)); 1371618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL7)); 1381618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL8)); 1391618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL9)); 1401618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL10)); 1411618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL11)); 1421618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL12)); 1431618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL13)); 1441618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL14)); 1451618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL15)); 1461618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL16)); 1471618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL17)); 1481618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL18)); 1491618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL19)); 1501618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL20)); 1513492Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL21)); 1524168Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL22)); 1534168Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL23)); 1544665Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL24)); 1555411Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL25)); 156*9273SAli.Bahrami@Sun.COM (void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL26)); 1574168Sab196087 } 1584168Sab196087 1594168Sab196087 /* 1606635Sab196087 * Output a block of raw data as hex bytes. Each row is given 1616635Sab196087 * the index of the first byte in the row. 1626635Sab196087 * 1636635Sab196087 * entry: 1646635Sab196087 * data - Pointer to first byte of data to be displayed 1656635Sab196087 * n - # of bytes of data 1666635Sab196087 * prefix - String to be output before each line. Useful 1676635Sab196087 * for indenting output. 1686635Sab196087 * bytes_per_col - # of space separated bytes to output 1696635Sab196087 * in each column. 1706635Sab196087 * col_per_row - # of columns to output per row 1716635Sab196087 * 1726635Sab196087 * exit: 1736635Sab196087 * The formatted data has been sent to stdout. Each row of output 1746635Sab196087 * shows (bytes_per_col * col_per_row) bytes of data. 1756635Sab196087 */ 1766635Sab196087 void 1779085SAli.Bahrami@Sun.COM dump_hex_bytes(const void *data, size_t n, int indent, 1786635Sab196087 int bytes_per_col, int col_per_row) 1796635Sab196087 { 1809085SAli.Bahrami@Sun.COM const uchar_t *ldata = data; 1816635Sab196087 int bytes_per_row = bytes_per_col * col_per_row; 1826635Sab196087 int ndx, byte, word; 1836635Sab196087 char string[128], *str = string; 1846635Sab196087 char index[MAXNDXSIZE]; 1856635Sab196087 int index_width; 1866635Sab196087 int sp_prefix = 0; 1876635Sab196087 1886635Sab196087 1896635Sab196087 /* 1906635Sab196087 * Determine the width to use for the index string. We follow 1916635Sab196087 * 8-byte tab rules, but don't use an actual \t character so 1926635Sab196087 * that the output can be arbitrarily shifted without odd 1936635Sab196087 * tab effects, and so that all the columns line up no matter 1946635Sab196087 * how many lines of output are produced. 1956635Sab196087 */ 1966635Sab196087 ndx = n / bytes_per_row; 1976635Sab196087 (void) snprintf(index, sizeof (index), 1986635Sab196087 MSG_ORIG(MSG_FMT_INDEX2), EC_WORD(ndx)); 1996635Sab196087 index_width = strlen(index); 2006635Sab196087 index_width = S_ROUND(index_width, 8); 2016635Sab196087 2029085SAli.Bahrami@Sun.COM for (ndx = byte = word = 0; n > 0; n--, ldata++) { 2036635Sab196087 while (sp_prefix-- > 0) 2046635Sab196087 *str++ = ' '; 2056635Sab196087 2066635Sab196087 (void) snprintf(str, sizeof (string), 2079085SAli.Bahrami@Sun.COM MSG_ORIG(MSG_HEXDUMP_TOK), (int)*ldata); 2086635Sab196087 str += 2; 2096635Sab196087 sp_prefix = 1; 2106635Sab196087 2116635Sab196087 if (++byte == bytes_per_col) { 2126635Sab196087 sp_prefix += 2; 2136635Sab196087 word++; 2146635Sab196087 byte = 0; 2156635Sab196087 } 2166635Sab196087 if (word == col_per_row) { 2176635Sab196087 *str = '\0'; 2186635Sab196087 (void) snprintf(index, sizeof (index), 2196635Sab196087 MSG_ORIG(MSG_FMT_INDEX2), EC_WORD(ndx)); 2206635Sab196087 dbg_print(0, MSG_ORIG(MSG_HEXDUMP_ROW), 2216635Sab196087 indent, MSG_ORIG(MSG_STR_EMPTY), 2226635Sab196087 index_width, index, string); 2236635Sab196087 sp_prefix = 0; 2246635Sab196087 word = 0; 2256635Sab196087 ndx += bytes_per_row; 2266635Sab196087 str = string; 2276635Sab196087 } 2286635Sab196087 } 2296635Sab196087 if (byte || word) { 2306635Sab196087 *str = '\0'; /* */ 2316635Sab196087 (void) snprintf(index, sizeof (index), 2326635Sab196087 MSG_ORIG(MSG_FMT_INDEX2), EC_WORD(ndx)); 2336635Sab196087 dbg_print(0, MSG_ORIG(MSG_HEXDUMP_ROW), indent, 2346635Sab196087 MSG_ORIG(MSG_STR_EMPTY), index_width, index, string); 2356635Sab196087 } 2366635Sab196087 } 2376635Sab196087 2386635Sab196087 /* 2394168Sab196087 * Convert the ASCII representation of an index, or index range, into 2404168Sab196087 * binary form, and store it in rec: 2414168Sab196087 * 2424168Sab196087 * index: An positive or 0 valued integer 2434168Sab196087 * range: Two indexes, separated by a ':' character, denoting 2444168Sab196087 * a range of allowed values. If the second value is omitted, 2454168Sab196087 * any values equal to or greater than the first will match. 2464168Sab196087 * 2474168Sab196087 * exit: 2485411Sab196087 * On success, *rec is filled in with a MATCH_OPT_NDX or MATCH_OPT_RANGE 2494168Sab196087 * value, and this function returns (1). On failure, the contents 2504168Sab196087 * of *rec are undefined, and (0) is returned. 2514168Sab196087 */ 2524168Sab196087 int 2535411Sab196087 process_index_opt(const char *str, match_rec_t *rec) 2544168Sab196087 { 2554168Sab196087 #define SKIP_BLANK for (; *str && isspace(*str); str++) 2564168Sab196087 2574168Sab196087 char *endptr; 2584168Sab196087 2594168Sab196087 rec->value.ndx.start = strtol(str, &endptr, 10); 2604168Sab196087 /* Value must use some of the input, and be 0 or positive */ 2614168Sab196087 if ((str == endptr) || (rec->value.ndx.start < 0)) 2624168Sab196087 return (0); 2634168Sab196087 str = endptr; 2644168Sab196087 2654168Sab196087 SKIP_BLANK; 2664168Sab196087 if (*str != ':') { 2675411Sab196087 rec->opt_type = MATCH_OPT_NDX; 2684168Sab196087 } else { 2694168Sab196087 str++; /* Skip the ':' */ 2705411Sab196087 rec->opt_type = MATCH_OPT_RANGE; 2714168Sab196087 SKIP_BLANK; 2724168Sab196087 if (*str == '\0') { 2734168Sab196087 rec->value.ndx.end = -1; /* Indicates "to end" */ 2744168Sab196087 } else { 2754168Sab196087 rec->value.ndx.end = strtol(str, &endptr, 10); 2764168Sab196087 if ((str == endptr) || (rec->value.ndx.end < 0)) 2774168Sab196087 return (0); 2784168Sab196087 str = endptr; 2794168Sab196087 SKIP_BLANK; 2804168Sab196087 } 2814168Sab196087 } 2824168Sab196087 2834168Sab196087 /* Syntax error if anything is left over */ 2844168Sab196087 if (*str != '\0') 2854168Sab196087 return (0); 2864168Sab196087 2874168Sab196087 return (1); 2884168Sab196087 2894168Sab196087 #undef SKIP_BLANK 2904168Sab196087 } 2914168Sab196087 2924168Sab196087 /* 293*9273SAli.Bahrami@Sun.COM * Convert a string containing a specific type of ELF constant, or an ASCII 294*9273SAli.Bahrami@Sun.COM * representation of a number, to an integer. Strings starting with '0' 2955411Sab196087 * are taken to be octal, those staring with '0x' are hex, and all 2965411Sab196087 * others are decimal. 2975411Sab196087 * 2985411Sab196087 * entry: 2995411Sab196087 * str - String to be converted 300*9273SAli.Bahrami@Sun.COM * ctype - Constant type 3015411Sab196087 * v - Address of variable to receive resulting value. 3025411Sab196087 * 3035411Sab196087 * exit: 3045411Sab196087 * On success, returns True (1) and *v is set to the value. 3055411Sab196087 * On failure, returns False (0) and *v is undefined. 3065411Sab196087 */ 307*9273SAli.Bahrami@Sun.COM typedef enum { 308*9273SAli.Bahrami@Sun.COM ATOUI_PT, 309*9273SAli.Bahrami@Sun.COM ATOUI_SHT, 310*9273SAli.Bahrami@Sun.COM ATOUI_OSABI 311*9273SAli.Bahrami@Sun.COM } atoui_type_t; 312*9273SAli.Bahrami@Sun.COM 3135411Sab196087 static int 314*9273SAli.Bahrami@Sun.COM atoui(const char *str, atoui_type_t type, uint32_t *v) 3155411Sab196087 { 316*9273SAli.Bahrami@Sun.COM conv_strtol_uvalue_t uvalue; 317*9273SAli.Bahrami@Sun.COM char *endptr; 3185411Sab196087 319*9273SAli.Bahrami@Sun.COM if (conv_iter_strtol_init(str, &uvalue) != 0) { 320*9273SAli.Bahrami@Sun.COM switch (type) { 321*9273SAli.Bahrami@Sun.COM case ATOUI_PT: 322*9273SAli.Bahrami@Sun.COM if (conv_iter_phdr_type(CONV_OSABI_ALL, CONV_FMT_ALT_CF, 323*9273SAli.Bahrami@Sun.COM conv_iter_strtol, &uvalue) == CONV_ITER_DONE) 324*9273SAli.Bahrami@Sun.COM break; 325*9273SAli.Bahrami@Sun.COM (void) conv_iter_phdr_type(CONV_OSABI_ALL, 326*9273SAli.Bahrami@Sun.COM CONV_FMT_ALT_NF, conv_iter_strtol, &uvalue); 327*9273SAli.Bahrami@Sun.COM break; 328*9273SAli.Bahrami@Sun.COM case ATOUI_SHT: 329*9273SAli.Bahrami@Sun.COM if (conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL, 330*9273SAli.Bahrami@Sun.COM CONV_FMT_ALT_CF, conv_iter_strtol, &uvalue) == 331*9273SAli.Bahrami@Sun.COM CONV_ITER_DONE) 332*9273SAli.Bahrami@Sun.COM break; 333*9273SAli.Bahrami@Sun.COM (void) conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL, 334*9273SAli.Bahrami@Sun.COM CONV_FMT_ALT_NF, conv_iter_strtol, &uvalue); 335*9273SAli.Bahrami@Sun.COM break; 336*9273SAli.Bahrami@Sun.COM case ATOUI_OSABI: 337*9273SAli.Bahrami@Sun.COM if (conv_iter_ehdr_osabi(CONV_FMT_ALT_CF, 338*9273SAli.Bahrami@Sun.COM conv_iter_strtol, &uvalue) == CONV_ITER_DONE) 339*9273SAli.Bahrami@Sun.COM break; 340*9273SAli.Bahrami@Sun.COM (void) conv_iter_ehdr_osabi(CONV_FMT_ALT_NF, 341*9273SAli.Bahrami@Sun.COM conv_iter_strtol, &uvalue); 342*9273SAli.Bahrami@Sun.COM break; 343*9273SAli.Bahrami@Sun.COM } 344*9273SAli.Bahrami@Sun.COM if (uvalue.csl_found) { 345*9273SAli.Bahrami@Sun.COM *v = uvalue.csl_value; 346*9273SAli.Bahrami@Sun.COM return (1); 347*9273SAli.Bahrami@Sun.COM } 348*9273SAli.Bahrami@Sun.COM } 3495411Sab196087 3505411Sab196087 *v = strtoull(str, &endptr, 0); 3515411Sab196087 3525411Sab196087 /* If the left over part contains anything but whitespace, fail */ 3535411Sab196087 for (; *endptr; endptr++) 3545411Sab196087 if (!isspace(*endptr)) 3555411Sab196087 return (0); 3565411Sab196087 return (1); 3575411Sab196087 } 3585411Sab196087 3595411Sab196087 /* 3605411Sab196087 * Called after getopt() processing is finished if there is a non-empty 3615411Sab196087 * match list. Prepares the matching code for use. 3625411Sab196087 * 3635411Sab196087 * exit: 3645411Sab196087 * Returns True (1) if no errors are encountered. Writes an 3655411Sab196087 * error string to stderr and returns False (0) otherwise. 3665411Sab196087 */ 3675411Sab196087 static int 3685411Sab196087 match_prepare(char *argv0, uint_t flags) 3695411Sab196087 { 3705411Sab196087 match_rec_t *list; 3715411Sab196087 const char *str; 3725411Sab196087 int minus_p = (flags & FLG_SHOW_PHDR) != 0; 373*9273SAli.Bahrami@Sun.COM atoui_type_t atoui_type; 3745411Sab196087 3755411Sab196087 /* 3765411Sab196087 * Flag ambiguous attempt to use match option with both -p and 3775411Sab196087 * and one or more section SHOW options. In this case, we 3785411Sab196087 * can't tell what type of item we're supposed to match against. 3795411Sab196087 */ 3805411Sab196087 if (minus_p && (flags & FLG_MASK_SHOW_SHDR)) { 3815411Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_AMBIG_MATCH), 3825411Sab196087 basename(argv0)); 3835411Sab196087 return (0); 3845411Sab196087 } 3855411Sab196087 3865411Sab196087 /* Set the match type, based on the presence of the -p option */ 3875411Sab196087 if (minus_p) { 3885411Sab196087 match_state.item_type = MATCH_ITEM_PT; 389*9273SAli.Bahrami@Sun.COM atoui_type = ATOUI_PT; 3905411Sab196087 } else { 3915411Sab196087 match_state.item_type = MATCH_ITEM_SHT; 392*9273SAli.Bahrami@Sun.COM atoui_type = ATOUI_SHT; 3935411Sab196087 } 3945411Sab196087 3955411Sab196087 /* 3965411Sab196087 * Scan match list and perform any necessary fixups: 3975411Sab196087 * 3985411Sab196087 * MATCH_OPT_NAME: If -p is specified, convert MATCH_OPT_NAME (-N) 3995411Sab196087 * requests into MATCH_OPT_TYPE (-T). 4005411Sab196087 * 4015411Sab196087 * MATCH_OPT_TYPE: Now that we know item type we are matching 4025411Sab196087 * against, we can convert the string saved in the name 4035411Sab196087 * field during getopt() processing into an integer and 4045411Sab196087 * write it into the type field. 4055411Sab196087 */ 4065411Sab196087 for (list = match_state.list; list; list = list->next) { 4075411Sab196087 if ((list->opt_type == MATCH_OPT_NAME) && minus_p) 4085411Sab196087 list->opt_type = MATCH_OPT_TYPE; 4095411Sab196087 4105411Sab196087 if (list->opt_type != MATCH_OPT_TYPE) 4115411Sab196087 continue; 4125411Sab196087 4135411Sab196087 str = list->value.name; 414*9273SAli.Bahrami@Sun.COM if (atoui(str, atoui_type, &list->value.type) == 0) { 4155411Sab196087 const char *fmt = minus_p ? 4165411Sab196087 MSG_INTL(MSG_ERR_BAD_T_PT) : 4175411Sab196087 MSG_INTL(MSG_ERR_BAD_T_SHT); 4185411Sab196087 4195411Sab196087 (void) fprintf(stderr, fmt, basename(argv0), str); 4205411Sab196087 return (0); 4215411Sab196087 } 4225411Sab196087 } 4235411Sab196087 4245411Sab196087 return (1); 4255411Sab196087 } 4265411Sab196087 4275411Sab196087 4285411Sab196087 /* 4294168Sab196087 * Returns True (1) if the item with the given name or index should 4304168Sab196087 * be displayed, and False (0) if it should not be. 4314168Sab196087 * 4324168Sab196087 * entry: 4335411Sab196087 * match_flags - Bitmask specifying matching options, as described 4345411Sab196087 * in _elfdump.h. 4355411Sab196087 * name - If MATCH_F_NAME flag is set, name of item under 4365411Sab196087 * consideration. Otherwise ignored. 4374168Sab196087 * should not be considered. 4385411Sab196087 * ndx - If MATCH_F_NDX flag is set, index of item under consideration. 4395411Sab196087 * type - If MATCH_F_TYPE is set, type of item under consideration. 4405411Sab196087 * If MATCH_F_PHDR is set, this would be a program 4415411Sab196087 * header type (PT_). Otherwise, a section header type (SHT_). 4424168Sab196087 * 4434168Sab196087 * exit: 4444168Sab196087 * True will be returned if the given name/index matches those given 4455411Sab196087 * by one of the (-I, -N -T) command line options, or if no such option 4465411Sab196087 * was used in the command invocation and MATCH_F_STRICT is not 4475411Sab196087 * set. 4484168Sab196087 */ 4494168Sab196087 int 4505411Sab196087 match(match_flags_t match_flags, const char *name, uint_t ndx, uint_t type) 4514168Sab196087 { 4525411Sab196087 match_item_t item_type = (match_flags & MATCH_F_PHDR) ? 4535411Sab196087 MATCH_ITEM_PT : MATCH_ITEM_SHT; 4545411Sab196087 match_rec_t *list; 4554168Sab196087 4565411Sab196087 /* 4575411Sab196087 * If there is no match list, then we use the MATCH_F_STRICT 4585411Sab196087 * flag to decide what to return. In the strict case, we return 4595411Sab196087 * False (0), in the normal case, True (1). 4605411Sab196087 */ 4615411Sab196087 if (match_state.list == NULL) 4625411Sab196087 return ((match_flags & MATCH_F_STRICT) == 0); 4635411Sab196087 4645411Sab196087 /* 4655411Sab196087 * If item being checked is not the current match type, 4665411Sab196087 * then allow it. 4675411Sab196087 */ 4685411Sab196087 if (item_type != match_state.item_type) 4694168Sab196087 return (1); 4704168Sab196087 4714168Sab196087 /* Run through the match records and check for a hit */ 4725411Sab196087 for (list = match_state.list; list; list = list->next) { 4735411Sab196087 switch (list->opt_type) { 4745411Sab196087 case MATCH_OPT_NAME: 4755411Sab196087 if (((match_flags & MATCH_F_NAME) == 0) || 4765411Sab196087 (name == NULL)) 4775411Sab196087 break; 4785411Sab196087 if (strcmp(list->value.name, name) == 0) 4794168Sab196087 return (1); 4804168Sab196087 break; 4815411Sab196087 case MATCH_OPT_NDX: 4825411Sab196087 if ((match_flags & MATCH_F_NDX) && 4835411Sab196087 (ndx == list->value.ndx.start)) 4844168Sab196087 return (1); 4854168Sab196087 break; 4865411Sab196087 case MATCH_OPT_RANGE: 4874168Sab196087 /* 4884168Sab196087 * A range end value less than 0 means that any value 4894168Sab196087 * above the start is acceptible. 4904168Sab196087 */ 4915411Sab196087 if ((match_flags & MATCH_F_NDX) && 4925411Sab196087 (ndx >= list->value.ndx.start) && 4934168Sab196087 ((list->value.ndx.end < 0) || 4944168Sab196087 (ndx <= list->value.ndx.end))) 4954168Sab196087 return (1); 4964168Sab196087 break; 4975411Sab196087 4985411Sab196087 case MATCH_OPT_TYPE: 4995411Sab196087 if ((match_flags & MATCH_F_TYPE) && 5005411Sab196087 (type == list->value.type)) 5015411Sab196087 return (1); 5025411Sab196087 break; 5034168Sab196087 } 5044168Sab196087 } 5054168Sab196087 5064168Sab196087 /* Nothing matched */ 5074168Sab196087 return (0); 5084168Sab196087 } 5094168Sab196087 5104168Sab196087 /* 5115411Sab196087 * Add an entry to match_state.list for use by match(). This routine is for 5125411Sab196087 * use during getopt() processing. It should not be called once 5135411Sab196087 * match_prepare() has been called. 5144168Sab196087 * 5154168Sab196087 * Return True (1) for success. On failure, an error is written 5164168Sab196087 * to stderr, and False (0) is returned. 5174168Sab196087 */ 5184168Sab196087 static int 5195411Sab196087 add_match_record(char *argv0, match_rec_t *data) 5204168Sab196087 { 5215411Sab196087 match_rec_t *rec; 5225411Sab196087 match_rec_t *list; 5234168Sab196087 5244168Sab196087 if ((rec = malloc(sizeof (*rec))) == NULL) { 5254168Sab196087 int err = errno; 5264168Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_MALLOC), 5274168Sab196087 basename(argv0), strerror(err)); 5284168Sab196087 return (0); 5294168Sab196087 } 5304168Sab196087 5314168Sab196087 *rec = *data; 5324168Sab196087 5335411Sab196087 /* Insert at end of match_state.list */ 5345411Sab196087 if (match_state.list == NULL) { 5355411Sab196087 match_state.list = rec; 5364168Sab196087 } else { 5375411Sab196087 for (list = match_state.list; list->next != NULL; 5385411Sab196087 list = list->next) 5394168Sab196087 ; 5404168Sab196087 list->next = rec; 5414168Sab196087 } 5424168Sab196087 5434168Sab196087 rec->next = NULL; 5444168Sab196087 return (1); 5451618Srie } 5461618Srie 5475411Sab196087 static int 5485411Sab196087 decide(const char *file, int fd, Elf *elf, uint_t flags, 549*9273SAli.Bahrami@Sun.COM const char *wname, int wfd, uchar_t osabi) 5501618Srie { 5515411Sab196087 int r; 5525411Sab196087 5531618Srie if (gelf_getclass(elf) == ELFCLASS64) 554*9273SAli.Bahrami@Sun.COM r = regular64(file, fd, elf, flags, wname, wfd, osabi); 5551618Srie else 556*9273SAli.Bahrami@Sun.COM r = regular32(file, fd, elf, flags, wname, wfd, osabi); 5575411Sab196087 5585411Sab196087 return (r); 5591618Srie } 5601618Srie 5615411Sab196087 static int 5625411Sab196087 archive(const char *file, int fd, Elf *elf, uint_t flags, 563*9273SAli.Bahrami@Sun.COM const char *wname, int wfd, uchar_t osabi) 5641618Srie { 5651618Srie Elf_Cmd cmd = ELF_C_READ; 5661618Srie Elf_Arhdr *arhdr; 5679085SAli.Bahrami@Sun.COM Elf *_elf = NULL; 5681618Srie size_t ptr; 5699085SAli.Bahrami@Sun.COM Elf_Arsym *arsym = NULL; 5701618Srie 5711618Srie /* 5723492Sab196087 * Determine if the archive symbol table itself is required. 5731618Srie */ 5745411Sab196087 if ((flags & FLG_SHOW_SYMBOLS) && 5755411Sab196087 match(MATCH_F_NAME, MSG_ORIG(MSG_ELF_ARSYM), 0, 0)) { 5761618Srie /* 5771618Srie * Get the archive symbol table. 5781618Srie */ 5791618Srie if (((arsym = elf_getarsym(elf, &ptr)) == 0) && elf_errno()) { 5801618Srie /* 5811618Srie * The arsym could be 0 even though there was no error. 5821618Srie * Print the error message only when there was 5831618Srie * real error from elf_getarsym(). 5841618Srie */ 5851618Srie failure(file, MSG_ORIG(MSG_ELF_GETARSYM)); 5865411Sab196087 return (0); 5871618Srie } 5881618Srie } 5891618Srie 5901618Srie /* 5911618Srie * Print the archive symbol table only when the archive symbol 5921618Srie * table exists and it was requested to print. 5931618Srie */ 5941618Srie if (arsym) { 5951618Srie size_t cnt; 5961618Srie char index[MAXNDXSIZE]; 5971618Srie size_t offset = 0, _offset = 0; 5981618Srie 5991618Srie /* 6001618Srie * Print out all the symbol entries. 6011618Srie */ 6021618Srie dbg_print(0, MSG_INTL(MSG_ARCHIVE_SYMTAB)); 6031618Srie dbg_print(0, MSG_INTL(MSG_ARCHIVE_FIELDS)); 6041618Srie 6051618Srie for (cnt = 0; cnt < ptr; cnt++, arsym++) { 6061618Srie /* 6071618Srie * For each object obtain an elf descriptor so that we 6081618Srie * can establish the members name. Note, we have had 6091618Srie * archives where the archive header has not been 6101618Srie * obtainable so be lenient with errors. 6111618Srie */ 6121618Srie if ((offset == 0) || ((arsym->as_off != 0) && 6131618Srie (arsym->as_off != _offset))) { 6141618Srie 6151618Srie if (_elf) 6161618Srie (void) elf_end(_elf); 6171618Srie 6181618Srie if (elf_rand(elf, arsym->as_off) != 6191618Srie arsym->as_off) { 6201618Srie failure(file, MSG_ORIG(MSG_ELF_RAND)); 6219085SAli.Bahrami@Sun.COM arhdr = NULL; 6221618Srie } else if ((_elf = elf_begin(fd, 6231618Srie ELF_C_READ, elf)) == 0) { 6241618Srie failure(file, MSG_ORIG(MSG_ELF_BEGIN)); 6259085SAli.Bahrami@Sun.COM arhdr = NULL; 6261618Srie } else if ((arhdr = elf_getarhdr(_elf)) == 0) { 6271618Srie failure(file, 6281618Srie MSG_ORIG(MSG_ELF_GETARHDR)); 6299085SAli.Bahrami@Sun.COM arhdr = NULL; 6301618Srie } 6311618Srie 6321618Srie _offset = arsym->as_off; 6331618Srie if (offset == 0) 6341618Srie offset = _offset; 6351618Srie } 6361618Srie 6371618Srie (void) snprintf(index, MAXNDXSIZE, 6381618Srie MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(cnt)); 6391618Srie if (arsym->as_off) 6401618Srie dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM1), index, 6411618Srie /* LINTED */ 6421618Srie (int)arsym->as_off, arhdr ? arhdr->ar_name : 6431618Srie MSG_INTL(MSG_STR_UNKNOWN), (arsym->as_name ? 6441618Srie demangle(arsym->as_name, flags) : 6451618Srie MSG_INTL(MSG_STR_NULL))); 6461618Srie else 6471618Srie dbg_print(0, MSG_ORIG(MSG_FMT_ARSYM2), index, 6481618Srie /* LINTED */ 6491618Srie (int)arsym->as_off); 6501618Srie } 6511618Srie 6521618Srie if (_elf) 6531618Srie (void) elf_end(_elf); 6541618Srie 6551618Srie /* 6561618Srie * If we only need the archive symbol table return. 6571618Srie */ 6585411Sab196087 if ((flags & FLG_SHOW_SYMBOLS) && 6595411Sab196087 match(MATCH_F_STRICT | MATCH_F_NAME, 6605411Sab196087 MSG_ORIG(MSG_ELF_ARSYM), -1, -1)) 6615411Sab196087 return (0); 6621618Srie 6631618Srie /* 6641618Srie * Reset elf descriptor in preparation for processing each 6651618Srie * member. 6661618Srie */ 6671618Srie if (offset) 6681618Srie (void) elf_rand(elf, offset); 6691618Srie } 6701618Srie 6711618Srie /* 6721618Srie * Process each object within the archive. 6731618Srie */ 6741618Srie while ((_elf = elf_begin(fd, cmd, elf)) != NULL) { 6751618Srie char name[MAXPATHLEN]; 6761618Srie 6771618Srie if ((arhdr = elf_getarhdr(_elf)) == NULL) { 6781618Srie failure(file, MSG_ORIG(MSG_ELF_GETARHDR)); 6795411Sab196087 return (0); 6801618Srie } 6811618Srie if (*arhdr->ar_name != '/') { 6821618Srie (void) snprintf(name, MAXPATHLEN, 6831618Srie MSG_ORIG(MSG_FMT_ARNAME), file, arhdr->ar_name); 6841618Srie dbg_print(0, MSG_ORIG(MSG_FMT_NLSTR), name); 6851618Srie 6861618Srie switch (elf_kind(_elf)) { 6871618Srie case ELF_K_AR: 6885411Sab196087 if (archive(name, fd, _elf, flags, 689*9273SAli.Bahrami@Sun.COM wname, wfd, osabi) == 1) 6905411Sab196087 return (1); 6911618Srie break; 6921618Srie case ELF_K_ELF: 6935411Sab196087 if (decide(name, fd, _elf, flags, 694*9273SAli.Bahrami@Sun.COM wname, wfd, osabi) == 1) 6955411Sab196087 return (1); 6961618Srie break; 6971618Srie default: 6981618Srie (void) fprintf(stderr, 6991618Srie MSG_INTL(MSG_ERR_BADFILE), name); 7001618Srie break; 7011618Srie } 7021618Srie } 7031618Srie 7041618Srie cmd = elf_next(_elf); 7051618Srie (void) elf_end(_elf); 7061618Srie } 7075411Sab196087 7085411Sab196087 return (0); 7091618Srie } 7101618Srie 7111618Srie int 7121618Srie main(int argc, char **argv, char **envp) 7131618Srie { 7141618Srie Elf *elf; 7151618Srie int var, fd, wfd = 0; 7165411Sab196087 char *wname = NULL; 7171618Srie uint_t flags = 0; 7185411Sab196087 match_rec_t match_data; 7195411Sab196087 int ret; 720*9273SAli.Bahrami@Sun.COM uchar_t osabi; 7211618Srie 7221618Srie /* 7231618Srie * If we're on a 64-bit kernel, try to exec a full 64-bit version of 7241618Srie * the binary. If successful, conv_check_native() won't return. 7251618Srie */ 7262647Srie (void) conv_check_native(argv, envp); 7271618Srie 7281618Srie /* 7291618Srie * Establish locale. 7301618Srie */ 7311618Srie (void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY)); 7321618Srie (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS)); 7331618Srie 7341618Srie (void) setvbuf(stdout, NULL, _IOLBF, 0); 7351618Srie (void) setvbuf(stderr, NULL, _IOLBF, 0); 7361618Srie 7371618Srie opterr = 0; 7381618Srie while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) { 7391618Srie switch (var) { 7401618Srie case 'C': 7415411Sab196087 flags |= FLG_CTL_DEMANGLE; 7421618Srie break; 7431618Srie case 'c': 7445411Sab196087 flags |= FLG_SHOW_SHDR; 7451618Srie break; 7461618Srie case 'd': 7475411Sab196087 flags |= FLG_SHOW_DYNAMIC; 7481618Srie break; 7491618Srie case 'e': 7505411Sab196087 flags |= FLG_SHOW_EHDR; 7511618Srie break; 7521618Srie case 'G': 7535411Sab196087 flags |= FLG_SHOW_GOT; 7541618Srie break; 7551618Srie case 'g': 7565411Sab196087 flags |= FLG_SHOW_GROUP; 7571618Srie break; 7581618Srie case 'H': 7595411Sab196087 flags |= FLG_SHOW_CAP; 7601618Srie break; 7611618Srie case 'h': 7625411Sab196087 flags |= FLG_SHOW_HASH; 7631618Srie break; 7644168Sab196087 case 'I': 7655411Sab196087 if (!process_index_opt(optarg, &match_data)) 7665411Sab196087 goto usage_brief; 7674168Sab196087 if (!add_match_record(argv[0], &match_data)) 7684168Sab196087 return (1); 7695411Sab196087 flags |= FLG_CTL_MATCH; 7704168Sab196087 break; 7711618Srie case 'i': 7725411Sab196087 flags |= FLG_SHOW_INTERP; 7731618Srie break; 7741618Srie case 'k': 7755411Sab196087 flags |= FLG_CALC_CHECKSUM; 7761618Srie break; 7771618Srie case 'l': 7785411Sab196087 flags |= FLG_CTL_LONGNAME; 7791618Srie break; 7801618Srie case 'm': 7815411Sab196087 flags |= FLG_SHOW_MOVE; 7821618Srie break; 7831618Srie case 'N': 7845411Sab196087 match_data.opt_type = MATCH_OPT_NAME; 7854168Sab196087 match_data.value.name = optarg; 7864168Sab196087 if (!add_match_record(argv[0], &match_data)) 7874168Sab196087 return (1); 7885411Sab196087 flags |= FLG_CTL_MATCH; 7891618Srie break; 7901618Srie case 'n': 7915411Sab196087 flags |= FLG_SHOW_NOTE; 7921618Srie break; 793*9273SAli.Bahrami@Sun.COM case 'O': 794*9273SAli.Bahrami@Sun.COM { 795*9273SAli.Bahrami@Sun.COM uint32_t val; 796*9273SAli.Bahrami@Sun.COM 797*9273SAli.Bahrami@Sun.COM /* 798*9273SAli.Bahrami@Sun.COM * osabi is a uchar_t in the ELF header. 799*9273SAli.Bahrami@Sun.COM * Don't accept any value that exceeds 800*9273SAli.Bahrami@Sun.COM * that range. 801*9273SAli.Bahrami@Sun.COM */ 802*9273SAli.Bahrami@Sun.COM if ((atoui(optarg, ATOUI_OSABI, &val) == 0) || 803*9273SAli.Bahrami@Sun.COM (val > 255)) { 804*9273SAli.Bahrami@Sun.COM (void) fprintf(stderr, 805*9273SAli.Bahrami@Sun.COM MSG_INTL(MSG_ERR_BAD_T_OSABI), 806*9273SAli.Bahrami@Sun.COM basename(argv[0]), optarg); 807*9273SAli.Bahrami@Sun.COM return (1); 808*9273SAli.Bahrami@Sun.COM } 809*9273SAli.Bahrami@Sun.COM osabi = val; 810*9273SAli.Bahrami@Sun.COM } 811*9273SAli.Bahrami@Sun.COM flags |= FLG_CTL_OSABI; 812*9273SAli.Bahrami@Sun.COM break; 8134665Sab196087 case 'P': 8145411Sab196087 flags |= FLG_CTL_FAKESHDR; 8154665Sab196087 break; 8161618Srie case 'p': 8175411Sab196087 flags |= FLG_SHOW_PHDR; 8181618Srie break; 8191618Srie case 'r': 8205411Sab196087 flags |= FLG_SHOW_RELOC; 8211618Srie break; 8223492Sab196087 case 'S': 8235411Sab196087 flags |= FLG_SHOW_SORT; 8243492Sab196087 break; 8251618Srie case 's': 8265411Sab196087 flags |= FLG_SHOW_SYMBOLS; 8275411Sab196087 break; 8285411Sab196087 case 'T': 8295411Sab196087 /* 8305411Sab196087 * We can't evaluate the value yet, because 8315411Sab196087 * we need to know if -p is used or not in 8325411Sab196087 * order to tell if we're seeing section header 8335411Sab196087 * or program header types. So, we save the 8345411Sab196087 * string in the name field, and then convert 8355411Sab196087 * it to a type integer in a following pass. 8365411Sab196087 */ 8375411Sab196087 match_data.opt_type = MATCH_OPT_TYPE; 8385411Sab196087 match_data.value.name = optarg; 8395411Sab196087 if (!add_match_record(argv[0], &match_data)) 8405411Sab196087 return (1); 8415411Sab196087 flags |= FLG_CTL_MATCH; 8421618Srie break; 8431618Srie case 'u': 8445411Sab196087 flags |= FLG_SHOW_UNWIND; 8451618Srie break; 8461618Srie case 'v': 8475411Sab196087 flags |= FLG_SHOW_VERSIONS; 8481618Srie break; 8491618Srie case 'w': 8501618Srie wname = optarg; 8511618Srie break; 8521618Srie case 'y': 8535411Sab196087 flags |= FLG_SHOW_SYMINFO; 8541618Srie break; 8551618Srie case '?': 8561618Srie (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), 8571618Srie basename(argv[0])); 8581618Srie detail_usage(); 8591618Srie return (1); 8601618Srie default: 8611618Srie break; 8621618Srie } 8631618Srie } 8641618Srie 8655411Sab196087 /* -p and -w are mutually exclusive. -w only works with sections */ 8665411Sab196087 if (((flags & FLG_SHOW_PHDR) != 0) && (wname != NULL)) 8675411Sab196087 goto usage_brief; 8685411Sab196087 8695411Sab196087 /* If a match argument is present, prepare the match state */ 8705411Sab196087 if ((match_state.list != NULL) && (match_prepare(argv[0], flags) == 0)) 8715411Sab196087 return (1); 8725411Sab196087 8731618Srie /* 8745411Sab196087 * Decide what to do if no options specifying something to 8755411Sab196087 * show or do are present. 8765411Sab196087 * 8775411Sab196087 * If there is no -w and no match options, then we will set all 8785411Sab196087 * the show flags, causing a full display of everything in the 8795411Sab196087 * file that we know how to handle. 8805411Sab196087 * 8815411Sab196087 * Otherwise, if there is no match list, we generate a usage 8825411Sab196087 * error and quit. 8835411Sab196087 * 8845411Sab196087 * In the case where there is a match list, we go ahead and call 8855411Sab196087 * regular() anyway, leaving it to decide what to do. If -w is 8865411Sab196087 * present, regular() will use the match list to handle it. 8875411Sab196087 * In addition, in the absence of explicit show/calc flags, regular() 8885411Sab196087 * will compare the section headers to the match list and use 8895411Sab196087 * that to generate the FLG_ bits that will display the information 8905411Sab196087 * specified by the match list. 8911618Srie */ 8925411Sab196087 if ((flags & ~FLG_MASK_CTL) == 0) { 8935411Sab196087 if (!wname && (match_state.list == NULL)) 8945411Sab196087 flags |= FLG_MASK_SHOW; 8955411Sab196087 else if (match_state.list == NULL) 8965411Sab196087 goto usage_brief; 8971618Srie } 8981618Srie 8995411Sab196087 /* There needs to be at least 1 filename left following the options */ 9005411Sab196087 if ((var = argc - optind) == 0) 9015411Sab196087 goto usage_brief; 9021618Srie 9031618Srie /* 9041618Srie * If the -l/-C option is specified, set up the liblddbg.so. 9051618Srie */ 9065411Sab196087 if (flags & FLG_CTL_LONGNAME) 9071618Srie dbg_desc->d_extra |= DBG_E_LONG; 9085411Sab196087 if (flags & FLG_CTL_DEMANGLE) 9091618Srie dbg_desc->d_extra |= DBG_E_DEMANGLE; 9101618Srie 9111618Srie /* 9121618Srie * If the -w option has indicated an output file open it. It's 9131618Srie * arguable whether this option has much use when multiple files are 9141618Srie * being processed. 9155411Sab196087 * 9165411Sab196087 * If wname is non-NULL, we know that -p was not specified, due 9175411Sab196087 * to the test above. 9181618Srie */ 9191618Srie if (wname) { 9201618Srie if ((wfd = open(wname, (O_RDWR | O_CREAT | O_TRUNC), 9211618Srie 0666)) < 0) { 9221618Srie int err = errno; 9231618Srie (void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN), 9241618Srie wname, strerror(err)); 9255411Sab196087 return (1); 9261618Srie } 9271618Srie } 9281618Srie 9291618Srie /* 9305411Sab196087 * Open the input file, initialize the elf interface, and 9315411Sab196087 * process it. 9321618Srie */ 9335411Sab196087 ret = 0; 9345411Sab196087 for (; (optind < argc) && (ret == 0); optind++) { 9351618Srie const char *file = argv[optind]; 9361618Srie 9371618Srie if ((fd = open(argv[optind], O_RDONLY)) == -1) { 9381618Srie int err = errno; 9391618Srie (void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN), 9401618Srie file, strerror(err)); 9411618Srie continue; 9421618Srie } 9431618Srie (void) elf_version(EV_CURRENT); 9441618Srie if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 9451618Srie failure(file, MSG_ORIG(MSG_ELF_BEGIN)); 9461618Srie (void) close(fd); 9471618Srie continue; 9481618Srie } 9491618Srie 9501618Srie if (var > 1) 9511618Srie dbg_print(0, MSG_ORIG(MSG_FMT_NLSTRNL), file); 9521618Srie 9531618Srie switch (elf_kind(elf)) { 9541618Srie case ELF_K_AR: 955*9273SAli.Bahrami@Sun.COM ret = archive(file, fd, elf, flags, wname, wfd, osabi); 9561618Srie break; 9571618Srie case ELF_K_ELF: 958*9273SAli.Bahrami@Sun.COM ret = decide(file, fd, elf, flags, wname, wfd, osabi); 9591618Srie break; 9601618Srie default: 9611618Srie (void) fprintf(stderr, MSG_INTL(MSG_ERR_BADFILE), file); 9621618Srie break; 9631618Srie } 9641618Srie 9651618Srie (void) close(fd); 9661618Srie (void) elf_end(elf); 9671618Srie } 9681618Srie 9691618Srie if (wfd) 9701618Srie (void) close(wfd); 9715411Sab196087 return (ret); 9725411Sab196087 9735411Sab196087 usage_brief: 9745411Sab196087 /* Control comes here for a simple usage message and exit */ 9755411Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF), 9765411Sab196087 basename(argv[0])); 9775411Sab196087 return (1); 9785411Sab196087 9791618Srie } 980