/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include /* * This module uses shared code for several of the commands. * It is sometimes necessary to know which specific command * is active. */ typedef enum { SYMINFO_CMD_T_DUMP = 0, /* syminfo:dump */ SYMINFO_CMD_T_SI_BOUNDTO = 1, /* syminfo:si_boundto */ SYMINFO_CMD_T_SI_FLAGS = 2 /* syminfo:si_boundto */ } SYMINFO_CMD_T; #ifndef _ELF64 /* * We supply this function for the msg module. Only one copy is needed. */ const char * _syminfo_msg(Msg mid) { return (gettext(MSG_ORIG(mid))); } #endif /* * This function is supplied to elfedit through our elfedit_module_t * definition. It translates the opaque elfedit_i18nhdl_t handles * in our module interface into the actual strings for elfedit to * use. * * note: * This module uses Msg codes for its i18n handle type. * So the translation is simply to use MSG_INTL() to turn * it into a string and return it. */ static const char * mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl) { Msg msg = (Msg)hdl; return (MSG_INTL(msg)); } /* * The sym_opt_t enum specifies a bit value for every optional * argument allowed by a command in this module. */ typedef enum { SYMINFO_OPT_F_AND = 1, /* -and: AND (&) values to dest */ SYMINFO_OPT_F_CMP = 2, /* -cmp: Complement (~) values */ SYMINFO_OPT_F_NEEDED = 4, /* -needed: arg is name of object to */ /* be referenced via DT_NEEDED */ /* dynamic entry */ SYMINFO_OPT_F_OR = 8, /* -or: OR (|) values to dest */ SYMINFO_OPT_F_SYMNDX = 16 /* -symndx: Sym specified by index */ } syminfo_opt_t; /* * A variable of type ARGSTATE is used by each command to maintain * information about the syminfo section being used, as and for any * auxiliary sections that are related to it. This helps us to ensure * that we only fetch each section a single time: * - More efficient * - Prevents multiple ELFEDIT_MSG_DEBUG messages from * being produced for a given section. */ typedef struct { elfedit_obj_state_t *obj_state; syminfo_opt_t optmask; /* Mask of options used */ int argc; /* # of plain arguments */ const char **argv; /* Plain arguments */ struct { /* Syminfo */ elfedit_section_t *sec; Syminfo *data; Word n; } syminfo; struct { /* Symbol table */ elfedit_section_t *sec; Sym *data; Word n; } sym; struct { /* String table */ elfedit_section_t *sec; } str; struct { /* Dynamic section */ elfedit_section_t *sec; Dyn *data; Word n; } dynamic; } ARGSTATE; /* * Standard argument processing for syminfo module * * entry * obj_state, argc, argv - Standard command arguments * optmask - Mask of allowed optional arguments. * argstate - Address of ARGSTATE block to be initialized * * exit: * On success, *argstate is initialized. On error, * an error is issued and this routine does not return. * * note: * Only the syminfo section is initially referenced by * argstate. Use the argstate_add_XXX() routines below to * access any other sections needed. */ static void process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[], SYMINFO_CMD_T cmd, ARGSTATE *argstate) { elfedit_getopt_state_t getopt_state; elfedit_getopt_ret_t *getopt_ret; bzero(argstate, sizeof (*argstate)); argstate->obj_state = obj_state; elfedit_getopt_init(&getopt_state, &argc, &argv); /* Add each new option to the options mask */ while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) argstate->optmask |= getopt_ret->gor_idmask; /* * Usage error if there are too many plain arguments. * - syminfo:dump accepts a single argument * - syminfo:si_boundto accepts 2 arguments * - syminfo:si_flags accepts an unbounded number */ if (((cmd == SYMINFO_CMD_T_DUMP) && (argc > 1)) || ((cmd == SYMINFO_CMD_T_SI_BOUNDTO) && (argc > 2))) elfedit_command_usage(); /* If there may be an arbitrary amount of output, use a pager */ if (argc == 0) elfedit_pager_init(); /* Return the updated values of argc/argv */ argstate->argc = argc; argstate->argv = argv; /* Locate the syminfo section */ argstate->syminfo.sec = elfedit_sec_getsyminfo(obj_state, &argstate->syminfo.data, &argstate->syminfo.n); } /* * We maintain the state of the current syminfo table in a ARGSTATE * structure. A syminfo is related to the dynamic symbol table, and * can reference the dynamic section of the object. We don't look those * things up unless we actually need them, both to be efficient, and * to prevent duplicate ELFEDIT_MSG_DEBUG messages from being issued * as they are located. Hence, process_args() is used to initialze the * state block with just the syminfo section, and then one of the * argstate_add_XXX() functions is used as needed to fetch the * additional sections. * * entry: * argstate - State block for current symbol table. * * exit: * If the needed auxiliary section is not found, an error is * issued and the argstate_add_XXX() routine does not return. * Otherwise, the fields in argstate have been filled in, ready * for use. * */ static void argstate_add_sym(ARGSTATE *argstate) { if (argstate->sym.sec != NULL) return; argstate->sym.sec = elfedit_sec_getsymtab(argstate->obj_state, 1, argstate->syminfo.sec->sec_shdr->sh_link, NULL, &argstate->sym.data, &argstate->sym.n, NULL); } static void argstate_add_str(ARGSTATE *argstate) { if (argstate->str.sec != NULL) return; argstate_add_sym(argstate); argstate->str.sec = elfedit_sec_getstr(argstate->obj_state, argstate->sym.sec->sec_shdr->sh_link); } static void argstate_add_dynamic(ARGSTATE *argstate) { if (argstate->dynamic.sec != NULL) return; argstate->dynamic.sec = elfedit_sec_getdyn(argstate->obj_state, &argstate->dynamic.data, &argstate->dynamic.n); } /* * Display syminfo section entries in the style used by elfdump. * * entry: * argstate - State block for current symbol table. * ndx - Index of first symbol to display * cnt - Number of symbols to display */ static void dump_syminfo(ARGSTATE *argstate, Word ndx, Word cnt) { Syminfo *syminfo; Sym *sym; Dyn *dyn; syminfo = argstate->syminfo.data + ndx; argstate_add_sym(argstate); sym = argstate->sym.data + ndx; argstate_add_str(argstate); argstate_add_dynamic(argstate); dyn = argstate->dynamic.data; /* * Loop through the syminfo entries. */ Elf_syminfo_title(0); for (; cnt-- > 0; ndx++, syminfo++, sym++) { const char *needed = NULL, *name; name = elfedit_offset_to_str(argstate->str.sec, sym->st_name, ELFEDIT_MSG_ERR, 0); if ((syminfo->si_boundto < SYMINFO_BT_LOWRESERVE) && (syminfo->si_boundto < argstate->dynamic.n) && ((dyn[syminfo->si_boundto].d_tag == DT_NEEDED) || (dyn[syminfo->si_boundto].d_tag == DT_USED))) needed = elfedit_offset_to_str(argstate->str.sec, dyn[syminfo->si_boundto].d_un.d_val, ELFEDIT_MSG_ERR, 0); else needed = MSG_ORIG(MSG_STR_EMPTY); Elf_syminfo_entry(0, ndx, syminfo, name, needed); } } /* * Print syminfo values, taking the calling command, and output style * into account. * * entry: * cmd - SYMINFO_CMD_T_* value giving identify of caller * autoprint - If True, output is only produced if the elfedit * autoprint flag is set. If False, output is always produced. * argstate - State block for current symbol table. * ndx - Index of first symbol to display * cnt - Number of symbols to display */ static void print_syminfo(SYMINFO_CMD_T cmd, int autoprint, ARGSTATE *argstate, Word ndx, Word cnt) { elfedit_outstyle_t outstyle; Syminfo *syminfo; if ((autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0)) || (cnt == 0)) return; /* * Pick an output style. syminfo:dump is required to use the default * style. The other commands use the current output style. */ outstyle = (cmd == SYMINFO_CMD_T_DUMP) ? ELFEDIT_OUTSTYLE_DEFAULT : elfedit_outstyle(); /* * If doing default output, use elfdump style where we * show all symbol attributes. In this case, the command * that called us doesn't matter */ if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) { dump_syminfo(argstate, ndx, cnt); return; } syminfo = argstate->syminfo.data; switch (cmd) { case SYMINFO_CMD_T_SI_BOUNDTO: if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) { /* Find the dynamic section and string table */ argstate_add_dynamic(argstate); argstate_add_str(argstate); } for (syminfo += ndx; cnt--; syminfo++) { Half bndto = syminfo->si_boundto; if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) { const char *str = NULL; switch (bndto) { case SYMINFO_BT_SELF: str = elfedit_atoconst_value_to_str( ELFEDIT_CONST_SYMINFO_BT, SYMINFO_BT_SELF, 1); break; case SYMINFO_BT_PARENT: str = elfedit_atoconst_value_to_str( ELFEDIT_CONST_SYMINFO_BT, SYMINFO_BT_PARENT, 1); break; case SYMINFO_BT_NONE: str = elfedit_atoconst_value_to_str( ELFEDIT_CONST_SYMINFO_BT, SYMINFO_BT_NONE, 1); break; } if ((str == NULL) && (bndto < SYMINFO_BT_LOWRESERVE) && (argstate->dynamic.sec != NULL) && (bndto < argstate->dynamic.n) && (argstate->dynamic.data[bndto].d_tag == DT_NEEDED)) str = elfedit_offset_to_str( argstate->str.sec, argstate->dynamic.data[bndto]. d_un.d_val, ELFEDIT_MSG_ERR, 0); if (str != NULL) { elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), str); continue; } } /* * If we reach this point, we are either in numeric * mode, or we were unable to find a string above. * In either case, output as integer. */ elfedit_printf(MSG_ORIG(MSG_FMT_WORDVALNL), EC_WORD(bndto)); } break; case SYMINFO_CMD_T_SI_FLAGS: for (syminfo += ndx; cnt--; syminfo++) { if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) { Conv_syminfo_flags_buf_t buf; elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), conv_syminfo_flags(syminfo->si_flags, CONV_FMT_NOBKT, &buf)); } else { elfedit_printf(MSG_ORIG(MSG_FMT_HEXNUMNL), EC_WORD(syminfo->si_flags)); } } break; } } /* * Convert the given argument string into a symbol table index. * * entry: * argstate - State block for current symbol table. * arg - String containing symbol index argument. * * exit: * On success, returns the symbol index. On failure, an error * is issued and this routine does not return. */ static Word arg_to_symndx(ARGSTATE *argstate, const char *arg) { Word symndx; /* * If the -symndx option was specified, arg is an index * into the symbol table. */ if (argstate->optmask & SYMINFO_OPT_F_SYMNDX) return (elfedit_atoui_range(arg, MSG_ORIG(MSG_STR_SYM), 0, argstate->syminfo.n - 1, NULL)); /* * arg is a symbol name. Return the index of the first symbol * that matches */ argstate_add_sym(argstate); argstate_add_str(argstate); (void) elfedit_name_to_symndx(argstate->sym.sec, argstate->str.sec, arg, ELFEDIT_MSG_ERR, &symndx); return (symndx); } /* * Given a string argument representing an object, return the index of * the dynamic section that should be used for the si_boundto value. */ static Half needed_to_boundto(ARGSTATE *argstate, const char *arg) { Conv_inv_buf_t inv_buf; elfedit_dyn_elt_t strpad_elt; elfedit_dyn_elt_t null_elt; elfedit_section_t *dynsec; Word null_cnt; Dyn *dyn; Word str_offset, ndx, numdyn; int have_string; argstate_add_str(argstate); argstate_add_dynamic(argstate); dynsec = argstate->dynamic.sec; numdyn = argstate->dynamic.n; /* Locate DT_SUNW_STRPAD element if present and locate the DT_NULLs */ elfedit_dyn_elt_init(&strpad_elt); elfedit_dyn_elt_init(&null_elt); null_cnt = 0; strpad_elt.dn_dyn.d_un.d_val = 0; dyn = argstate->dynamic.data; for (ndx = 0; ndx < numdyn; dyn++, ndx++) { switch (dyn->d_tag) { case DT_NULL: /* Count all the nulls, remember the first one */ null_cnt++; if (!null_elt.dn_seen) elfedit_dyn_elt_save(&null_elt, ndx, dyn); break; case DT_SUNW_STRPAD: elfedit_dyn_elt_save(&strpad_elt, ndx, dyn); break; } } /* * Look up the string in the string table and get its offset. If * this succeeds, then it is possible that there is a DT_NEEDED * dynamic entry that references it. */ have_string = elfedit_sec_findstr(argstate->str.sec, strpad_elt.dn_dyn.d_un.d_val, arg, &str_offset) != 0; if (have_string) { dyn = argstate->dynamic.data; for (ndx = 0; ndx < numdyn; dyn++, ndx++) { if (((dyn->d_tag == DT_NEEDED) || (dyn->d_tag == DT_USED)) && (dyn->d_un.d_val == str_offset)) goto done; } } /* * It doesn't already exist. We might be able to add a DT_NEEDED * to the dynamic section if an extra DT_NULL is available. * Otherwise, we have to fail here. */ if (null_cnt < 2) elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOEXTRANULL), EC_WORD(dynsec->sec_shndx), dynsec->sec_name); /* * If the string is not already in the string table, try to * insert it. If it succeeds, we will convert the DT_NULL. * Otherwise, an error will be issued and control will not * return here. */ if (!have_string) str_offset = elfedit_dynstr_insert(dynsec, argstate->str.sec, &strpad_elt, arg); /* Convert the extra DT_NULL */ ndx = null_elt.dn_ndx; elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CONVNULL), EC_WORD(dynsec->sec_shndx), dynsec->sec_name, EC_WORD(ndx), conv_dyn_tag(DT_NEEDED, argstate->obj_state->os_ehdr->e_machine, 0, &inv_buf)); dyn = argstate->dynamic.data + ndx; dyn->d_tag = DT_NEEDED; dyn->d_un.d_val = str_offset; elfedit_modified_data(dynsec); done: elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_FNDNEEDED), dynsec->sec_shndx, dynsec->sec_name, ndx, arg); return (ndx); } /* * Common body for the syminfo: module commands. These commands * share a large amount of common behavior, so it is convenient * to centralize things and use the cmd argument to handle the * small differences. * * entry: * cmd - One of the SYMINFO_CMD_T_* constants listed above, specifying * which command to implement. * obj_state, argc, argv - Standard command arguments */ static elfedit_cmdret_t cmd_body(SYMINFO_CMD_T cmd, elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { ARGSTATE argstate; Word ndx; Syminfo *syminfo; elfedit_cmdret_t ret = ELFEDIT_CMDRET_NONE; process_args(obj_state, argc, argv, cmd, &argstate); /* If there are no arguments, dump the whole table and return */ if (argstate.argc == 0) { print_syminfo(cmd, 0, &argstate, 0, argstate.syminfo.n); return (ELFEDIT_CMDRET_NONE); } /* The first argument is the symbol name/index */ ndx = arg_to_symndx(&argstate, argstate.argv[0]); /* If there is a single argument, display that item and return */ if (argstate.argc == 1) { print_syminfo(cmd, 0, &argstate, ndx, 1); return (ELFEDIT_CMDRET_NONE); } syminfo = &argstate.syminfo.data[ndx]; /* * Syminfo [0] holds the value SYMINFO_CURRENT, as a versioning * technique. You're not supposed to mess with it. */ if (ndx == 0) elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CHGSYMINFO0), EC_WORD(argstate.syminfo.sec->sec_shndx), argstate.syminfo.sec->sec_name, EC_WORD(ndx)); /* The second value supplies a new value for the item */ switch (cmd) { /* * SYMINFO_CMD_T_DUMP can't get here: It never has more than * one argument, and is handled above. */ case SYMINFO_CMD_T_SI_BOUNDTO: { const char *name = MSG_ORIG(MSG_CMD_SI_BOUNDTO); Half boundto; if (argstate.optmask & SYMINFO_OPT_F_NEEDED) boundto = needed_to_boundto(&argstate, argstate.argv[1]); else boundto = elfedit_atoconst_range( argstate.argv[1], MSG_ORIG(MSG_STR_VALUE), 0, 0xffff, ELFEDIT_CONST_SYMINFO_BT); if (syminfo->si_boundto == boundto) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_X_OK), argstate.syminfo.sec->sec_shndx, argstate.syminfo.sec->sec_name, ndx, name, syminfo->si_boundto); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_X_CHG), argstate.syminfo.sec->sec_shndx, argstate.syminfo.sec->sec_name, ndx, name, syminfo->si_boundto, boundto); ret = ELFEDIT_CMDRET_MOD; syminfo->si_boundto = boundto; } } break; case SYMINFO_CMD_T_SI_FLAGS: { Conv_syminfo_flags_buf_t flags_buf1, flags_buf2; const char *name = MSG_ORIG(MSG_CMD_SI_FLAGS); Half flags = 0; int i; /* Collect the arguments */ for (i = 1; i < argstate.argc; i++) flags |= (Word) elfedit_atoconst(argstate.argv[i], ELFEDIT_CONST_SYMINFO_FLG); /* Complement the value? */ if (argstate.optmask & SYMINFO_OPT_F_CMP) flags = ~flags; /* Perform any requested bit operations */ if (argstate.optmask & SYMINFO_OPT_F_AND) flags &= syminfo->si_flags; else if (argstate.optmask & SYMINFO_OPT_F_OR) flags |= syminfo->si_flags; /* Set the value */ if (syminfo->si_flags == flags) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK), argstate.syminfo.sec->sec_shndx, argstate.syminfo.sec->sec_name, ndx, name, conv_syminfo_flags(syminfo->si_flags, 0, &flags_buf1)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG), argstate.syminfo.sec->sec_shndx, argstate.syminfo.sec->sec_name, ndx, name, conv_syminfo_flags(syminfo->si_flags, 0, &flags_buf1), conv_syminfo_flags(flags, 0, &flags_buf2)); ret = ELFEDIT_CMDRET_MOD; syminfo->si_flags = flags; } } break; } /* * If we modified the syminfo section, tell libelf. */ if (ret == ELFEDIT_CMDRET_MOD) elfedit_modified_data(argstate.syminfo.sec); /* Do autoprint */ print_syminfo(cmd, 1, &argstate, ndx, 1); return (ret); } /* * Command completion functions for the various commands */ /*ARGSUSED*/ static void cpl_si_boundto(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { int i; /* * If -needed option is not present, the second argument can be * an SYMINFO_BT_ value. */ if (argc != (num_opt + 2)) return; /* Is -needed there? If so, no completion is possible so return */ for (i = 0; i < num_opt; i++) if (strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_NEEDED)) == 0) return; elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SYMINFO_BT); } /*ARGSUSED*/ static void cpl_si_flags(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { /* The second argument can be an SYMINFO_FLG_ value */ if (argc == (num_opt + 2)) elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SYMINFO_FLG); } /* * Implementation functions for the commands */ static elfedit_cmdret_t cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(SYMINFO_CMD_T_DUMP, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_si_boundto(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(SYMINFO_CMD_T_SI_BOUNDTO, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_si_flags(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(SYMINFO_CMD_T_SI_FLAGS, obj_state, argc, argv)); } /*ARGSUSED*/ elfedit_module_t * elfedit_init(elfedit_module_version_t version) { /* sym:dump */ static const char *name_dump[] = { MSG_ORIG(MSG_CMD_DUMP), MSG_ORIG(MSG_STR_EMPTY), /* "" makes this the default command */ NULL }; static elfedit_cmd_optarg_t opt_dump[] = { { MSG_ORIG(MSG_STR_MINUS_SYMNDX), /* MSG_INTL(MSG_OPTDESC_SYMNDX) */ ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0, SYMINFO_OPT_F_SYMNDX, 0 }, { NULL } }; static elfedit_cmd_optarg_t arg_dump[] = { { MSG_ORIG(MSG_STR_SYM), /* MSG_INTL(MSG_A1_SYM) */ ELFEDIT_I18NHDL(MSG_A1_SYM), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* sym:si_boundto */ static const char *name_si_boundto[] = { MSG_ORIG(MSG_CMD_SI_BOUNDTO), NULL }; static elfedit_cmd_optarg_t opt_si_boundto[] = { { MSG_ORIG(MSG_STR_MINUS_NEEDED), /* MSG_INTL(MSG_OPTDESC_NEEDED) */ ELFEDIT_I18NHDL(MSG_OPTDESC_NEEDED), 0, SYMINFO_OPT_F_NEEDED, 0 }, { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { MSG_ORIG(MSG_STR_MINUS_SYMNDX), /* MSG_INTL(MSG_OPTDESC_SYMNDX) */ ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0, SYMINFO_OPT_F_SYMNDX, 0 }, { NULL } }; static elfedit_cmd_optarg_t arg_si_boundto[] = { { MSG_ORIG(MSG_STR_SYM), /* MSG_INTL(MSG_A1_SYM) */ ELFEDIT_I18NHDL(MSG_A1_SYM), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A2_DESC_SI_BOUNDTO) */ ELFEDIT_I18NHDL(MSG_A2_DESC_SI_BOUNDTO), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* sym:si_flags */ static const char *name_si_flags[] = { MSG_ORIG(MSG_CMD_SI_FLAGS), NULL }; static elfedit_cmd_optarg_t opt_si_flags[] = { { ELFEDIT_STDOA_OPT_AND, NULL, ELFEDIT_CMDOA_F_INHERIT, SYMINFO_OPT_F_AND, SYMINFO_OPT_F_OR }, { ELFEDIT_STDOA_OPT_CMP, NULL, ELFEDIT_CMDOA_F_INHERIT, SYMINFO_OPT_F_CMP, 0 }, { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { ELFEDIT_STDOA_OPT_OR, NULL, ELFEDIT_CMDOA_F_INHERIT, SYMINFO_OPT_F_OR, SYMINFO_OPT_F_AND }, { MSG_ORIG(MSG_STR_MINUS_SYMNDX), /* MSG_INTL(MSG_OPTDESC_SYMNDX) */ ELFEDIT_I18NHDL(MSG_OPTDESC_SYMNDX), 0, SYMINFO_OPT_F_SYMNDX, 0 }, { NULL } }; static elfedit_cmd_optarg_t arg_si_flags[] = { { MSG_ORIG(MSG_STR_SYM), /* MSG_INTL(MSG_A1_SYM) */ ELFEDIT_I18NHDL(MSG_A1_SYM), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A2_DESC_SI_FLAGS) */ ELFEDIT_I18NHDL(MSG_A2_DESC_SI_FLAGS), ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT }, { NULL } }; static elfedit_cmd_t cmds[] = { /* sym:dump */ { cmd_dump, NULL, name_dump, /* MSG_INTL(MSG_DESC_DUMP) */ ELFEDIT_I18NHDL(MSG_DESC_DUMP), /* MSG_INTL(MSG_HELP_DUMP) */ ELFEDIT_I18NHDL(MSG_HELP_DUMP), opt_dump, arg_dump }, /* sym:si_boundto */ { cmd_si_boundto, cpl_si_boundto, name_si_boundto, /* MSG_INTL(MSG_DESC_SI_BOUNDTO) */ ELFEDIT_I18NHDL(MSG_DESC_SI_BOUNDTO), /* MSG_INTL(MSG_HELP_SI_BOUNDTO) */ ELFEDIT_I18NHDL(MSG_HELP_SI_BOUNDTO), opt_si_boundto, arg_si_boundto }, /* sym:si_flags */ { cmd_si_flags, cpl_si_flags, name_si_flags, /* MSG_INTL(MSG_DESC_SI_FLAGS) */ ELFEDIT_I18NHDL(MSG_DESC_SI_FLAGS), /* MSG_INTL(MSG_HELP_SI_FLAGS) */ ELFEDIT_I18NHDL(MSG_HELP_SI_FLAGS), opt_si_flags, arg_si_flags }, { NULL } }; static elfedit_module_t module = { ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_NAME), /* MSG_INTL(MSG_MOD_DESC) */ ELFEDIT_I18NHDL(MSG_MOD_DESC), cmds, mod_i18nhdl_to_str }; return (&module); }