/* * 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 2008 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 /* * Dynamic section */ /* * This module uses shared code for several of the commands. * It is sometimes necessary to know which specific command * is active. */ typedef enum { /* Dump command, used as module default to display dynamic section */ DYN_CMD_T_DUMP = 0, /* dyn:dump */ /* Commands that do not correspond directly to a specific DT tag */ DYN_CMD_T_TAG = 1, /* dyn:tag */ DYN_CMD_T_VALUE = 2, /* dyn:value */ DYN_CMD_T_DELETE = 3, /* dyn:delete */ DYN_CMD_T_MOVE = 4, /* dyn:shift */ /* Commands that embody tag specific knowledge */ DYN_CMD_T_RUNPATH = 5, /* dyn:runpath/rpath */ DYN_CMD_T_POSFLAG1 = 6, /* dyn:posflag1 */ DYN_CMD_T_FLAGS = 7, /* dyn:flags */ DYN_CMD_T_FLAGS1 = 8, /* dyn:flags1 */ DYN_CMD_T_FEATURE1 = 9, /* dyn:feature1 */ DYN_CMD_T_CHECKSUM = 10, /* dyn:checksum */ DYN_CMD_T_SUNW_LDMACH = 11 /* dyn:sunw_ldmach */ } DYN_CMD_T; #ifndef _ELF64 /* * We supply this function for the msg module */ const char * _dyn_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 dyn_opt_t enum specifies a bit value for every optional * argument allowed by a command in this module. */ typedef enum { DYN_OPT_F_ADD = 1, /* -add: Add new elt rather than */ /* modifying an existing one */ DYN_OPT_F_AND = 2, /* -and: AND (&) values to dest */ DYN_OPT_F_CMP = 4, /* -cmp: Complement (~) values */ DYN_OPT_F_DYNNDX_ELT = 8, /* -dynndx: 1st plain arg is tag */ /* index, not name */ DYN_OPT_F_DYNNDX_VAL = 16, /* -dynndx ndx: Index is value to */ /* option rather than 1st plain */ /* arg. Used for dyn:posflag1 */ DYN_OPT_F_NEEDED = 32, /* -needed str: Locate DT_POSFLAG_1 */ /* relative to DT_NEEDED element */ DYN_OPT_F_OR = 64, /* -or: OR (|) values to dest */ DYN_OPT_F_STRVAL = 128 /* -s: value is string, not integer */ } dyn_opt_t; /* * A variable of type ARGSTATE is used by each command to maintain * information about the arguments and related things. It is * initialized by process_args(), and used by the other routines. */ typedef struct { elfedit_obj_state_t *obj_state; elfedit_section_t *strsec; /* Dynamic string table ref */ struct { elfedit_section_t *sec; /* Dynamic section reference */ Dyn *data; /* Start dynamic section data */ Word num; /* # dynamic elts */ Word null_ndx; /* Index of first DT_NULL */ Word num_null_ndx; /* # of DT_NULL elements */ } dyn; dyn_opt_t optmask; /* Mask of options used */ int argc; /* # of plain arguments */ const char **argv; /* Plain arguments */ const char *dyn_elt_str; /* Value string for */ /* DYN_OPT_F_DYNNDX_VAL */ /* or DYN_OPT_F_NEEDED */ } ARGSTATE; /* * Set argstate null_ndx field for current dynamic area */ static void set_null_ndx(ARGSTATE *argstate) { Word num, null_ndx; num = argstate->dyn.num; argstate->dyn.num_null_ndx = 0; for (null_ndx = 0; null_ndx < num; null_ndx++) if (argstate->dyn.data[null_ndx].d_tag == DT_NULL) { argstate->dyn.num_null_ndx++; break; } argstate->dyn.null_ndx = null_ndx; /* Count the number of remaining DT_NULL items */ for (; null_ndx < num; null_ndx++) if (argstate->dyn.data[null_ndx].d_tag == DT_NULL) argstate->dyn.num_null_ndx++; } /* * Convert the first available DT_NULL slot in the dynamic section * into something else. * * entry: * argstate - Argument state block * d_tag, d_val - Values to be set in new element * * exit: * If an extra DT_NULL slot is available, a debug message is * issued, the slot is converted to its new use, and the argstate * block state related to DT_NULL slots is updated. * * if no extra DT_NULL slot is present, an error is issued and * this routine does not return to the caller. */ static Word convert_dt_null(ARGSTATE *argstate, Word d_tag, Xword d_val) { Conv_inv_buf_t inv_buf; Word ndx; Dyn *dyn; /* If we lack an extra element, we can't continue */ if (argstate->dyn.num_null_ndx <= 1) elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOEXTRANULL), EC_WORD(argstate->dyn.sec->sec_shndx), argstate->dyn.sec->sec_name); elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CONVNULL), EC_WORD(argstate->dyn.sec->sec_shndx), argstate->dyn.sec->sec_name, EC_WORD(argstate->dyn.null_ndx), conv_dyn_tag(d_tag, argstate->obj_state->os_ehdr->e_machine, 0, &inv_buf)); ndx = argstate->dyn.null_ndx; dyn = &argstate->dyn.data[ndx]; dyn->d_tag = d_tag; dyn->d_un.d_val = d_val; /* Recompute the DT_NULL situation */ set_null_ndx(argstate); return (ndx); } /* * Standard argument processing for dyn module * * entry * obj_state, argc, argv - Standard command 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. */ static void process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[], 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; switch (getopt_ret->gor_idmask) { case DYN_OPT_F_DYNNDX_VAL: case DYN_OPT_F_NEEDED: argstate->dyn_elt_str = getopt_ret->gor_value; break; } } /* 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 dynamic section, and the assocated string table */ argstate->dyn.sec = elfedit_sec_getdyn(obj_state, &argstate->dyn.data, &argstate->dyn.num); argstate->strsec = elfedit_sec_getstr(obj_state, argstate->dyn.sec->sec_shdr->sh_link, 0); /* Index of first DT_NULL */ set_null_ndx(argstate); } /* * Print ELF header values, taking the calling command, and output style * into account. * * entry: * cmd - DYN_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 - Argument state block * print_type - Specifies which dynamic elements to display. * ndx = If print_type is PRINT_DYN_T_NDX, displays the index specified. * Otherwise ignored. */ typedef enum { PRINT_DYN_T_ALL = 0, /* Show all indexes */ PRINT_DYN_T_NDX = 1, /* Show dynamic[arg] only */ PRINT_DYN_T_TAG = 2, /* Show all elts with tag type */ /* given by arg */ PRINT_DYN_T_RUNPATH = 3 /* Show all runpath/rpath elts */ } PRINT_DYN_T; static void print_dyn(DYN_CMD_T cmd, int autoprint, ARGSTATE *argstate, PRINT_DYN_T print_type, Word arg) { elfedit_outstyle_t outstyle; Conv_fmt_flags_t flags_fmt_flags; Word end_ndx, ndx, printed = 0; Dyn *dyn; int header_done = 0; Xword last_d_val; int one_shot; if (autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0)) return; /* * Pick an output style. dyn:dump is required to use the default * style. The other commands use the current output style. */ outstyle = (cmd == DYN_CMD_T_DUMP) ? ELFEDIT_OUTSTYLE_DEFAULT : elfedit_outstyle(); /* * When using the simple output style, omit the * brackets from around the values. */ flags_fmt_flags = (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) ? CONV_FMT_NOBKT : 0; /* Starting index */ if (print_type == PRINT_DYN_T_NDX) { if (arg >= argstate->dyn.num) return; /* Out of range */ ndx = arg; } else { ndx = 0; } /* * one_shot is used by positional elements (e.g. DT_POSFLAG_1) * to get the item following them to be shown even if they * are not of the desired tag type or the count of elements * to be displayed is only 1. */ one_shot = 0; dyn = &argstate->dyn.data[ndx]; /* * Loop predicate explanation: * Normally, we want to iterate from the starting index * to the end. However, in the case of PRINT_DYN_T_NDX, we * only want to display one item (ndx == arg) and then quit, * with the exception that if we've been through the loop * and encountered a one_shot situation, we want to continue * iterating until the one-shot situation is cleared. */ for (; (ndx < argstate->dyn.num) && ((print_type != PRINT_DYN_T_NDX) || ((ndx == arg) || one_shot)); dyn++, ndx++) { union { Conv_inv_buf_t inv; Conv_dyn_flag_buf_t flag; Conv_dyn_flag1_buf_t flag1; Conv_dyn_posflag1_buf_t posflag1; Conv_dyn_feature1_buf_t feature1; } c_buf; const char *name; if (one_shot) { one_shot = 0; } else { /* * If we are only displaying certain tag types and * this isn't one of those, move on to next element. */ switch (print_type) { case PRINT_DYN_T_TAG: if (dyn->d_tag != arg) continue; break; case PRINT_DYN_T_RUNPATH: if ((dyn->d_tag != DT_RPATH) && (dyn->d_tag != DT_RUNPATH)) continue; break; } } /* * Print the information numerically, and if possible * as a string. */ name = NULL; switch (dyn->d_tag) { case DT_NULL: if (!((outstyle == ELFEDIT_OUTSTYLE_DEFAULT) && (print_type == PRINT_DYN_T_ALL) && (dyn->d_un.d_val == 0))) break; end_ndx = ndx; /* * Special case: DT_NULLs can come in groups * that we prefer to reduce to a single line. */ while ((end_ndx < (argstate->dyn.num - 1)) && ((dyn + 1)->d_tag == DT_NULL) && ((dyn + 1)->d_un.d_val == 0)) { dyn++; end_ndx++; } if (header_done == 0) { header_done = 1; Elf_dyn_title(0); } Elf_dyn_null_entry(0, dyn, ndx, end_ndx); ndx = end_ndx; printed = 1; last_d_val = dyn->d_un.d_val; continue; /* * Print the information numerically, and if possible * as a string. */ case DT_NEEDED: case DT_SONAME: case DT_FILTER: case DT_AUXILIARY: case DT_CONFIG: case DT_RPATH: case DT_RUNPATH: case DT_USED: case DT_DEPAUDIT: case DT_AUDIT: case DT_SUNW_AUXILIARY: case DT_SUNW_FILTER: name = elfedit_offset_to_str(argstate->strsec, dyn->d_un.d_val, ELFEDIT_MSG_DEBUG, 0); break; case DT_FLAGS: name = conv_dyn_flag(dyn->d_un.d_val, flags_fmt_flags, &c_buf.flag); break; case DT_FLAGS_1: name = conv_dyn_flag1(dyn->d_un.d_val, flags_fmt_flags, &c_buf.flag1); break; case DT_POSFLAG_1: /* * If this is dyn:posflag1, and the print_type * is PRINT_DYN_T_TAG, and the -needed option is * used, then don't show any DT_POSFLAG_1 elements * that are not followed by a DT_NEEDED element * that matches the -needed string. */ if ((cmd == DYN_CMD_T_POSFLAG1) && (print_type == PRINT_DYN_T_TAG) && ((argstate->optmask & DYN_OPT_F_NEEDED) != 0) && ((ndx + 1) < argstate->dyn.num)) { Dyn *dyn1 = &argstate->dyn.data[ndx + 1]; if (dyn1->d_tag != DT_NEEDED) continue; name = elfedit_offset_to_str(argstate->strsec, dyn1->d_un.d_val, ELFEDIT_MSG_DEBUG, 0); if (strncmp(name, argstate->dyn_elt_str, strlen(argstate->dyn_elt_str)) != 0) continue; } name = conv_dyn_posflag1(dyn->d_un.d_val, flags_fmt_flags, &c_buf.posflag1); /* * DT_POSFLAG_1 is a positional element that affects * the following item. If using the default output * style, then show the following item as well. */ one_shot = (outstyle == ELFEDIT_OUTSTYLE_DEFAULT); break; case DT_FEATURE_1: name = conv_dyn_feature1(dyn->d_un.d_val, flags_fmt_flags, &c_buf.feature1); break; case DT_DEPRECATED_SPARC_REGISTER: name = MSG_INTL(MSG_STR_DEPRECATED); break; case DT_SUNW_LDMACH: name = conv_ehdr_mach((Half)dyn->d_un.d_val, 0, &c_buf.inv); break; } if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) { if (header_done == 0) { header_done = 1; Elf_dyn_title(0); } if (name == NULL) name = MSG_ORIG(MSG_STR_EMPTY); Elf_dyn_entry(0, dyn, ndx, name, argstate->obj_state->os_ehdr->e_machine); } else { /* * In simple or numeric mode under a print type * that is based on tag type rather than on index, * quietly: If we've already printed this value, * don't print it again. A common example of this * is PRINT_DYN_T_RUNPATH when both DT_RPATH and * DT_RUNPATH are present with the same value. */ switch (print_type) { case PRINT_DYN_T_TAG: /* * Positional flags don't count, because * each one affects a different item. So don't * skip those. */ if (dyn->d_tag != DT_POSFLAG_1) continue; break; case PRINT_DYN_T_RUNPATH: if (printed && (last_d_val == dyn->d_un.d_val)) continue; break; } if ((name != NULL) && (outstyle == ELFEDIT_OUTSTYLE_SIMPLE)) { elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), name); } else { elfedit_printf(MSG_ORIG(MSG_FMT_HEXXWORDNL), dyn->d_un.d_val); } } printed = 1; last_d_val = dyn->d_un.d_val; } /* * If nothing was output under the print types that are * based on tag type, issue an error saying it doesn't exist. */ if (!printed) { if (print_type == PRINT_DYN_T_TAG) { Conv_inv_buf_t inv_buf; elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NODYNELT), EC_WORD(argstate->dyn.sec->sec_shndx), argstate->dyn.sec->sec_name, conv_dyn_tag(arg, argstate->obj_state->os_ehdr->e_machine, 0, &inv_buf)); } if (print_type == PRINT_DYN_T_RUNPATH) elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NORUNPATH), EC_WORD(argstate->dyn.sec->sec_shndx), argstate->dyn.sec->sec_name); } } /* * Determine the index(s) of the dynamic element(s) to be displayed and/or * manipulated. * * entry: * argstate - Argument state block * arg - If the command being called accepts a first plain argument * named 'elt' which is used to specify the dynamic element, * arg is the value of argv[0] for that command. If the * command does not accept an 'elt' argument and instead * implicitly assumes a tag type, arg is the constant string * for that type (e.g. "DT_POSFLAG_1"). * print_request - True if the command is to print the current * value(s) and return without changing anything. * print_type - Address of variable containing PRINT_DYN_T_ * code specifying how the elements will be displayed. * * exit: * If print_request is False: This routine always returns the index * of a single dynamic element. *print_type is set to PRINT_DYN_T_NDX. * The 'elt' argument as well as any modifier options (-dynndx, -needed) * are examined to determine this index. If there are no modifier options, * the dynamic section contains no element of the desired type, and there * is an extra DT_NULL element in the section, then a new element of * the desired type is created and its index returned. Otherwise an * error is issued. * * If print_request is True: If a modifier (-dynndx, -needed) was used, * *print_type is set to PRINT_DYN_T_NDX and the index of the * corresponding single dynamic element is returned. If no modifier * was used, *print_type is set to PRINT_DYN_T_TAG, and the tag * type code is returned. */ static Word arg_to_index(ARGSTATE *argstate, const char *arg, int print_request, PRINT_DYN_T *print_type) { Word ndx, dt_value; Dyn *dyn; /* Assume we are returning an index, alter as needed below */ *print_type = PRINT_DYN_T_NDX; /* * All the commands that accept the DYN_OPT_F_DYNNDX_ELT form * of -dynndx require a plain argument named 'elt' as their first * argument. -dynndx is a modifier that means that 'elt' is a * simple numeric section index. Routines that accept this form * of -dynndx are willing to handle any tag type, so all we need * to check is that the value is in range. */ if ((argstate->optmask & DYN_OPT_F_DYNNDX_ELT) != 0) return ((Word) elfedit_atoui_range(arg, MSG_ORIG(MSG_STR_ELT), 0, argstate->dyn.num - 1, NULL)); /* arg is a DT_ tag type, not a numeric index */ dt_value = (Word) elfedit_atoconst(arg, ELFEDIT_CONST_DT); /* * Commands that accept the DYN_OPT_F_DYNNDX_VAL form of * dynndx do not accept the 'elt' argument. The index is a * value that follows the option, and was saved in argstate by * process_args(). Routines that accept this form of -dynndx * require the specified element to have a specific tag type, * so we test for this as well as for the index being in range. */ if ((argstate->optmask & DYN_OPT_F_DYNNDX_VAL) != 0) { ndx = ((Word) elfedit_atoui_range(argstate->dyn_elt_str, MSG_ORIG(MSG_STR_INDEX), 0, argstate->dyn.num - 1, NULL)); if (argstate->dyn.data[ndx].d_tag != dt_value) { Half mach = argstate->obj_state->os_ehdr->e_machine; Conv_inv_buf_t is, want; elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_WRONGTAG), EC_WORD(argstate->dyn.sec->sec_shndx), argstate->dyn.sec->sec_name, ndx, conv_dyn_tag(dt_value, mach, 0, &want), conv_dyn_tag(argstate->dyn.data[ndx].d_tag, mach, 0, &is)); } return (ndx); } /* * If this is a printing request, then we let print_dyn() show * all the items with this tag type. */ if (print_request) { *print_type = PRINT_DYN_T_TAG; return (dt_value); } /* * Commands that accept -needed are looking for the dt_value element * (usually DT_POSFLAG_1) that immediately preceeds the DT_NEEDED * element with the string given by argstate->dyn_elt_str. */ if ((argstate->optmask & DYN_OPT_F_NEEDED) != 0) { Word retndx = argstate->dyn.num; /* Out of range value */ const char *name; size_t len; len = strlen(argstate->dyn_elt_str); for (ndx = 0, dyn = argstate->dyn.data; ndx < argstate->dyn.num; dyn++, ndx++) { /* * If the immediately preceeding item has the * tag type we're looking for, and the current item * is a DT_NEEDED with a string that matches, * then the preceeding item is the one we want. */ if ((dyn->d_tag == DT_NEEDED) && (ndx > 0) && (retndx == (ndx - 1))) { name = elfedit_offset_to_str(argstate->strsec, dyn->d_un.d_val, ELFEDIT_MSG_DEBUG, 0); if (strncmp(name, argstate->dyn_elt_str, len) == 0) return (retndx); continue; } /* * If the current item has the tag type we're * looking for, make it our current candidate. * If the next item is a DT_NEEDED with the right * string value, we'll use it then. */ if (dyn->d_tag == dt_value) retndx = ndx; } /* If we get here, no matching DT_NEEDED was found */ elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NEEDEDNOMATCH), EC_WORD(argstate->dyn.sec->sec_shndx), argstate->dyn.sec->sec_name, argstate->dyn_elt_str); } /* Locate the first entry with the given tag type */ for (ndx = 0; ndx < argstate->dyn.num; ndx++) { if (argstate->dyn.data[ndx].d_tag == dt_value) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_DT2NDX), EC_WORD(argstate->dyn.sec->sec_shndx), argstate->dyn.sec->sec_name, EC_WORD(ndx), arg); return (ndx); } } /* Not found. Can we create one? */ if (argstate->dyn.num_null_ndx > 1) return (convert_dt_null(argstate, dt_value, 0)); /* No room to create one, so we're out of options and must fail */ elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NODTELT), EC_WORD(argstate->dyn.sec->sec_shndx), argstate->dyn.sec->sec_name, arg); /*NOTREACHED*/ return (0); /* For lint */ } /* * Called by cmd_body() for dyn:value. Implements the core functionality * for that command. * * This routine expects that both the index and value arguments are * present. */ static elfedit_cmdret_t cmd_body_value(ARGSTATE *argstate, Word *ret_ndx) { elfedit_section_t *dynsec = argstate->dyn.sec; elfedit_section_t *strsec = argstate->strsec; elfedit_dyn_elt_t strpad_elt; Word i; Dyn *dyn = argstate->dyn.data; Word numdyn = argstate->dyn.num; int minus_add, minus_s, minus_dynndx; Word arg1, tmp_val; Xword arg2; int arg2_known = 1; minus_add = ((argstate->optmask & DYN_OPT_F_ADD) != 0); minus_s = ((argstate->optmask & DYN_OPT_F_STRVAL) != 0); minus_dynndx = ((argstate->optmask & DYN_OPT_F_DYNNDX_ELT) != 0); elfedit_dyn_elt_init(&strpad_elt); /* * The first argument is an index if -dynndx is used, and is a * tag value otherwise. */ arg1 = minus_dynndx ? elfedit_atoui_range(argstate->argv[0], MSG_ORIG(MSG_STR_ELT), 0, numdyn - 1, NULL) : elfedit_atoconst(argstate->argv[0], ELFEDIT_CONST_DT); if (minus_s) { /* * Don't allow the user to specify -s when manipulating a * DT_SUNW_STRPAD element. Since DT_SUNW_STRPAD is used to * manage the extra space used for strings, this would break * our ability to add the string. */ if ((!minus_dynndx && (arg1 == DT_SUNW_STRPAD)) || (minus_dynndx && (dyn[arg1].d_tag == DT_SUNW_STRPAD))) elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_STRPADSTRVAL), EC_WORD(dynsec->sec_shndx), dynsec->sec_name); /* Locate DT_SUNW_STRPAD element if present */ strpad_elt.dn_dyn.d_un.d_val = 0; (void) elfedit_dynstr_getpad(argstate->dyn.sec, &strpad_elt); /* * Look up the string: If the user specified the -dynndx * -option, then we will insert it if possible, and * fail with an error if not. However, if they did not * specify -dynndx, we want to look up the string if it is * already there, but defer the insertion. The reason for * this is that we may have to grab an unused DT_NULL element * below, and if there are none available, we won't want * to have modified the string table. * * This isn't a problem, because if the string isn't * in the string table, it can't be used by a dynamic element. * Hence, we don't need to insert it to know that there is * no match. */ if (minus_dynndx == 0) { if (elfedit_sec_findstr(strsec, strpad_elt.dn_dyn.d_un.d_val, argstate->argv[1], &tmp_val) == 0) { arg2_known = 0; } else { arg2 = tmp_val; } } else { arg2 = elfedit_dynstr_insert(dynsec, strsec, &strpad_elt, argstate->argv[1]); } } else { /* Argument 2 is an integer */ arg2 = elfedit_atoui(argstate->argv[1], NULL); } if (!minus_dynndx && !(minus_add && !arg2_known)) { /* * Search the dynamic section and see if an item with the * specified tag value already exists. We can reduce this * to a simple update of an existing value if -add is not * specified or the existing d_un value matches the new one. * * In either of these cases, we will change arg1 to be the * index, and set minus_dynndx, causing the simple update to * happen immediately below. */ for (i = 0; i < numdyn; i++) { if ((dyn[i].d_tag == arg1) && (!minus_add || (dyn[i].d_un.d_val == arg2))) { arg1 = i; minus_dynndx = 1; break; } } } /* * If -dynndx is used, then this is a relatively simple * operation, as we simply write over the specified index. */ if (minus_dynndx) { /* * If we held back from inserting a new string into * the dynstr above, we insert it now, because we * have a slot in the dynamic section, and we need * the string offset ot finish. */ if (!arg2_known) arg2 = elfedit_dynstr_insert(dynsec, strsec, &strpad_elt, argstate->argv[1]); *ret_ndx = arg1; if (dyn[arg1].d_un.d_val == arg2) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_X_OK), dynsec->sec_shndx, dynsec->sec_name, EC_WORD(arg1), EC_XWORD(arg2)); return (ELFEDIT_CMDRET_NONE); } else { /* Warn if setting DT_NULL value to non-zero */ if ((dyn[arg1].d_tag == DT_NULL) && (arg2 != 0)) elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_DTNULLVALUE), dynsec->sec_shndx, dynsec->sec_name, EC_WORD(arg1), EC_XWORD(arg2)); elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_X_CHG), dynsec->sec_shndx, dynsec->sec_name, EC_WORD(arg1), EC_XWORD(dyn[arg1].d_un.d_val), EC_XWORD(arg2)); dyn[arg1].d_un.d_val = arg2; return (ELFEDIT_CMDRET_MOD); } } /* * We need a new slot in the dynamic section. If we can't have * one, then we fail. */ if (argstate->dyn.num_null_ndx <= 1) elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOEXTRANULL), EC_WORD(dynsec->sec_shndx), dynsec->sec_name); /* * If we still need to insert a new string into the dynstr, * then it is safe now, because if we succeed, we know that * there is an available slot to receive it. If we fail, we * haven't claimed the extra slot yet, and it will be unharmed. */ if (!arg2_known) arg2 = elfedit_dynstr_insert(dynsec, strsec, &strpad_elt, argstate->argv[1]); /* Use an extra DT_NULL slot and enter the new element */ *ret_ndx = convert_dt_null(argstate, arg1, arg2); return (ELFEDIT_CMDRET_MOD); } /* * Called by cmd_body() for dyn:runpath. Implements the core functionality * for that command. * * History Lesson And Strategy: * * This routine handles both DT_RPATH and DT_RUNPATH entries, altering * either or both if they are present. * * The original SYSV ABI only had DT_RPATH, and the runtime loader used * it to search for things in the following order: * * DT_RPATH, LD_LIBRARY_PATH, defaults * * Solaris did not follow this rule, an extremely rare deviation from * the ABI. Environment variables should supercede everything else, * otherwise they are not very useful. This decision was made at the * very beginning of the SunOS 5.x development, so we have always * deviated from the ABI and and instead search in the order * * LD_LIBRARY_PATH, DT_RPATH, defaults * * Other Unix variants initially followed the ABI, but in recent years * have come to agree with the early Solaris folks that it was a mistake. * Hence, DT_RUNPATH was invented, with the search order: * * LD_LIBRARY_PATH, DT_RUNPATH, defaults * * So for Solaris, DT_RPATH and DT_RUNPATH mean the same thing. If both * are present (which does happen), we set them both to the new * value. If either one is present, we set that one. If neither is * present, and we have a spare DT_NULL slot, we create a DT_RUNPATH, but * not a DT_RPATH, to conserve available slots for other uses. */ static elfedit_cmdret_t cmd_body_runpath(ARGSTATE *argstate) { elfedit_section_t *dynsec = argstate->dyn.sec; elfedit_section_t *strsec = argstate->strsec; elfedit_dyn_elt_t rpath_elt; elfedit_dyn_elt_t runpath_elt; elfedit_dyn_elt_t strpad_elt; Word i; Dyn *dyn = argstate->dyn.data; Word numdyn = argstate->dyn.num; /* Go through the tags and gather what we need */ elfedit_dyn_elt_init(&rpath_elt); elfedit_dyn_elt_init(&runpath_elt); elfedit_dyn_elt_init(&strpad_elt); for (i = 0; i < numdyn; i++) { switch (dyn[i].d_tag) { case DT_RPATH: elfedit_dyn_elt_save(&rpath_elt, i, &dyn[i]); break; case DT_RUNPATH: elfedit_dyn_elt_save(&runpath_elt, i, &dyn[i]); break; case DT_SUNW_STRPAD: elfedit_dyn_elt_save(&strpad_elt, i, &dyn[i]); break; } } /* Do we have an available dynamic section entry to use? */ if (rpath_elt.dn_seen || runpath_elt.dn_seen) { /* * We have seen a DT_RPATH, or a DT_RUNPATH, or both. * If all of these have the same string as the desired * new value, then we don't need to alter anything and can * simply return. Otherwise, we'll modify them all to have * the new string (below). */ if ((!rpath_elt.dn_seen || (strcmp(elfedit_dyn_offset_to_str(strsec, &rpath_elt), argstate->argv[0]) == 0)) && (!runpath_elt.dn_seen || (strcmp(elfedit_dyn_offset_to_str(strsec, &runpath_elt), argstate->argv[0]) == 0))) { if (rpath_elt.dn_seen) elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_OLDRPATHOK), EC_WORD(dynsec->sec_shndx), dynsec->sec_name, EC_WORD(rpath_elt.dn_ndx), elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_RPATH, 1)); if (runpath_elt.dn_seen) elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_OLDRPATHOK), EC_WORD(dynsec->sec_shndx), dynsec->sec_name, EC_WORD(runpath_elt.dn_ndx), elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_RUNPATH, 1)); return (ELFEDIT_CMDRET_NONE); } } else if (argstate->dyn.num_null_ndx <= 1) { /* * There is no DT_RPATH or DT_RUNPATH in the dynamic array, * and there are no extra DT_NULL entries that we can * convert into one. We cannot proceed. */ elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOEXTRANULL), EC_WORD(dynsec->sec_shndx), dynsec->sec_name); } /* Does the string exist in the table already, or can we add it? */ rpath_elt.dn_dyn.d_un.d_val = runpath_elt.dn_dyn.d_un.d_val = elfedit_dynstr_insert(dynsec, strsec, &strpad_elt, argstate->argv[0]); /* Update DT_RPATH entry if present */ if (rpath_elt.dn_seen) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_PREVRPATH), EC_WORD(dynsec->sec_shndx), dynsec->sec_name, EC_WORD(rpath_elt.dn_ndx), elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_RPATH, 1), elfedit_dyn_offset_to_str(strsec, &rpath_elt)); dyn[rpath_elt.dn_ndx] = rpath_elt.dn_dyn; } /* * Update the DT_RUNPATH entry in the dynamic section, if present. * If one is not present, and there is also no DT_RPATH, then * we use a spare DT_NULL entry to create a new DT_RUNPATH. */ if (runpath_elt.dn_seen || !rpath_elt.dn_seen) { if (runpath_elt.dn_seen) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_PREVRPATH), EC_WORD(dynsec->sec_shndx), dynsec->sec_name, EC_WORD(runpath_elt.dn_ndx), elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_RUNPATH, 1), elfedit_dyn_offset_to_str(strsec, &runpath_elt)); dyn[runpath_elt.dn_ndx] = runpath_elt.dn_dyn; } else { /* Using a spare DT_NULL entry */ (void) convert_dt_null(argstate, DT_RUNPATH, runpath_elt.dn_dyn.d_un.d_val); } } return (ELFEDIT_CMDRET_MOD); } /* * Argument processing for the bitmask commands. Convert the arguments * to integer form, apply -and/-cmp/-or, and return the resulting value. * * entry: * argstate - Argument state block * orig - Value of original bitmask * const_type - ELFEDIT_CONST_* value for type of constants */ static Word flag_bitop(ARGSTATE *argstate, Word orig, elfedit_const_t const_type) { Word flags = 0; int i; /* Collect the arguments */ for (i = 0; i < argstate->argc; i++) flags |= (Word) elfedit_atoconst(argstate->argv[i], const_type); /* Complement the value? */ if (argstate->optmask & DYN_OPT_F_CMP) flags = ~flags; /* Perform any requested bit operations */ if (argstate->optmask & DYN_OPT_F_AND) flags &= orig; else if (argstate->optmask & DYN_OPT_F_OR) flags |= orig; return (flags); } /* * Common body for the dyn: 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 DYN_CMD_T_* constants listed above, specifying * which command to implement. * obj_state, argc, argv - Standard command arguments */ static elfedit_cmdret_t cmd_body(DYN_CMD_T cmd, elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { ARGSTATE argstate; Dyn *dyn; const char *dyn_name; Word dyn_ndx, dyn_num, null_ndx; elfedit_cmdret_t ret = ELFEDIT_CMDRET_NONE; PRINT_DYN_T print_type = PRINT_DYN_T_ALL; Word ndx; int print_only = 0; int do_autoprint = 1; /* Process the optional arguments */ process_args(obj_state, argc, argv, &argstate); dyn = argstate.dyn.data; dyn_num = argstate.dyn.num; dyn_name = argstate.dyn.sec->sec_name; dyn_ndx = argstate.dyn.sec->sec_shndx; /* Check number of arguments, gather information */ switch (cmd) { case DYN_CMD_T_DUMP: /* dyn:dump can accept an optional index argument */ if (argstate.argc > 1) elfedit_command_usage(); print_only = 1; if (argstate.argc == 1) ndx = arg_to_index(&argstate, argstate.argv[0], print_only, &print_type); break; case DYN_CMD_T_TAG: print_only = (argstate.argc != 2); if (argstate.argc > 0) { if (argstate.argc > 2) elfedit_command_usage(); ndx = arg_to_index(&argstate, argstate.argv[0], print_only, &print_type); } break; case DYN_CMD_T_VALUE: print_only = (argstate.argc != 2); if (argstate.argc > 2) elfedit_command_usage(); if (argstate.argc > 0) { if (print_only) { ndx = arg_to_index(&argstate, argstate.argv[0], print_only, &print_type); } else { print_type = PRINT_DYN_T_NDX; } } break; case DYN_CMD_T_DELETE: if ((argstate.argc < 1) || (argstate.argc > 2)) elfedit_command_usage(); ndx = arg_to_index(&argstate, argstate.argv[0], 0, &print_type); do_autoprint = 0; break; case DYN_CMD_T_MOVE: if ((argstate.argc < 2) || (argstate.argc > 3)) elfedit_command_usage(); ndx = arg_to_index(&argstate, argstate.argv[0], 0, &print_type); do_autoprint = 0; break; case DYN_CMD_T_RUNPATH: if (argstate.argc > 1) elfedit_command_usage(); /* * dyn:runpath does not accept an explicit index * argument, so we implicitly only show the DT_RPATH and * DT_RUNPATH elements. */ print_type = PRINT_DYN_T_RUNPATH; print_only = (argstate.argc == 0); break; case DYN_CMD_T_POSFLAG1: print_only = (argstate.argc == 0); ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_POSFLAG_1, 1), print_only, &print_type); break; case DYN_CMD_T_FLAGS: print_only = (argstate.argc == 0); ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_FLAGS, 1), print_only, &print_type); break; case DYN_CMD_T_FLAGS1: print_only = (argstate.argc == 0); ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_FLAGS_1, 1), print_only, &print_type); break; case DYN_CMD_T_FEATURE1: print_only = (argstate.argc == 0); ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_FEATURE_1, 1), print_only, &print_type); break; case DYN_CMD_T_CHECKSUM: ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_CHECKSUM, 1), print_only, &print_type); break; case DYN_CMD_T_SUNW_LDMACH: if (argstate.argc > 1) elfedit_command_usage(); print_only = (argstate.argc == 0); ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str( ELFEDIT_CONST_DT, DT_SUNW_LDMACH, 1), print_only, &print_type); break; default: /* Note expected: All commands should have been caught above */ elfedit_command_usage(); break; } /* If this is a request to print current values, do it and return */ if (print_only) { print_dyn(cmd, 0, &argstate, print_type, ndx); return (ELFEDIT_CMDRET_NONE); } switch (cmd) { /* * DYN_CMD_T_DUMP can't get here: It is a print-only * command. */ case DYN_CMD_T_TAG: { Conv_inv_buf_t inv_buf1, inv_buf2; Half mach = argstate.obj_state->os_ehdr->e_machine; Word d_tag = (Word) elfedit_atoconst(argstate.argv[1], ELFEDIT_CONST_DT); if (dyn[ndx].d_tag == d_tag) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_tag(d_tag, mach, 0, &inv_buf1)); } else { Word orig_d_tag = dyn[ndx].d_tag; ret = ELFEDIT_CMDRET_MOD; dyn[ndx].d_tag = d_tag; /* * Update null termination index. Warn if we * just clobbered the only DT_NULL termination * for the array. */ null_ndx = argstate.dyn.null_ndx; set_null_ndx(&argstate); if ((argstate.dyn.null_ndx >= argstate.dyn.num) && (null_ndx != argstate.dyn.null_ndx)) elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NULLTERM), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_tag(d_tag, mach, 0, &inv_buf1)); /* * Warning if * - Inserting a DT_NULL cuts off following * non-null elements. * - Inserting a non-DT_NULL after the * first null element, will be * ignored by rtld. */ if (d_tag == DT_NULL) { if ((ndx + 1) < null_ndx) elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NULCLIP), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_tag(d_tag, mach, 0, &inv_buf1)); } else { if ((ndx + 1) > argstate.dyn.null_ndx) elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NULHIDE), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_tag(d_tag, mach, 0, &inv_buf1)); } /* Debug message that we changed it */ elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_tag(orig_d_tag, mach, 0, &inv_buf1), conv_dyn_tag(d_tag, mach, 0, &inv_buf2)); } } break; case DYN_CMD_T_VALUE: ret = cmd_body_value(&argstate, &ndx); break; case DYN_CMD_T_DELETE: { Word cnt = (argstate.argc == 1) ? 1 : (Word) elfedit_atoui_range(argstate.argv[1], MSG_ORIG(MSG_STR_COUNT), 1, dyn_num - ndx, NULL); const char *msg_prefix = elfedit_sec_msgprefix(argstate.dyn.sec); elfedit_array_elts_delete(msg_prefix, argstate.dyn.data, sizeof (Dyn), dyn_num, ndx, cnt); ret = ELFEDIT_CMDRET_MOD; } break; case DYN_CMD_T_MOVE: { Dyn save; Word cnt; Word dstndx; const char *msg_prefix = elfedit_sec_msgprefix(argstate.dyn.sec); dstndx = (Word) elfedit_atoui_range(argstate.argv[1], MSG_ORIG(MSG_STR_DST_INDEX), 0, dyn_num - 1, NULL); if (argstate.argc == 2) { cnt = 1; } else { cnt = (Word) elfedit_atoui_range( argstate.argv[2], MSG_ORIG(MSG_STR_COUNT), 1, dyn_num, NULL); } elfedit_array_elts_move(msg_prefix, argstate.dyn.data, sizeof (save), dyn_num, ndx, dstndx, cnt, &save); ret = ELFEDIT_CMDRET_MOD; } break; case DYN_CMD_T_RUNPATH: ret = cmd_body_runpath(&argstate); break; case DYN_CMD_T_POSFLAG1: { Conv_dyn_posflag1_buf_t buf1, buf2; Word flags; flags = flag_bitop(&argstate, dyn[ndx].d_un.d_val, ELFEDIT_CONST_DF_P1); /* Set the value */ if (dyn[ndx].d_un.d_val == flags) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_posflag1(dyn[ndx].d_un.d_val, 0, &buf1)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_posflag1(dyn[ndx].d_un.d_val, 0, &buf1), conv_dyn_posflag1(flags, 0, &buf2)); ret = ELFEDIT_CMDRET_MOD; dyn[ndx].d_un.d_val = flags; } } break; case DYN_CMD_T_FLAGS: { Conv_dyn_flag_buf_t buf1, buf2; Word flags; flags = flag_bitop(&argstate, dyn[ndx].d_un.d_val, ELFEDIT_CONST_DF); /* Set the value */ if (dyn[ndx].d_un.d_val == flags) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_flag(dyn[ndx].d_un.d_val, 0, &buf1)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_flag(dyn[ndx].d_un.d_val, 0, &buf1), conv_dyn_flag(flags, 0, &buf2)); ret = ELFEDIT_CMDRET_MOD; dyn[ndx].d_un.d_val = flags; } } break; case DYN_CMD_T_FLAGS1: { Conv_dyn_flag1_buf_t buf1, buf2; Word flags1; flags1 = flag_bitop(&argstate, dyn[ndx].d_un.d_val, ELFEDIT_CONST_DF_1); /* Set the value */ if (dyn[ndx].d_un.d_val == flags1) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_flag1(dyn[ndx].d_un.d_val, 0, &buf1)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_flag1(dyn[ndx].d_un.d_val, 0, &buf1), conv_dyn_flag1(flags1, 0, &buf2)); ret = ELFEDIT_CMDRET_MOD; dyn[ndx].d_un.d_val = flags1; } } break; case DYN_CMD_T_FEATURE1: { Conv_dyn_feature1_buf_t buf1, buf2; Word flags; flags = flag_bitop(&argstate, dyn[ndx].d_un.d_val, ELFEDIT_CONST_DTF_1); /* Set the value */ if (dyn[ndx].d_un.d_val == flags) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_feature1(dyn[ndx].d_un.d_val, 0, &buf1)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG), dyn_ndx, dyn_name, EC_WORD(ndx), conv_dyn_feature1(dyn[ndx].d_un.d_val, 0, &buf1), conv_dyn_feature1(flags, 0, &buf2)); ret = ELFEDIT_CMDRET_MOD; dyn[ndx].d_un.d_val = flags; } } break; case DYN_CMD_T_CHECKSUM: { long checksum = elf_checksum(obj_state->os_elf); /* Set the value */ if (dyn[ndx].d_un.d_val == checksum) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_X_OK), dyn_ndx, dyn_name, EC_WORD(ndx), EC_XWORD(checksum)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_X_CHG), dyn_ndx, dyn_name, EC_WORD(ndx), EC_XWORD(dyn[ndx].d_un.d_val), EC_XWORD(checksum)); ret = ELFEDIT_CMDRET_MOD; dyn[ndx].d_un.d_val = checksum; } } break; case DYN_CMD_T_SUNW_LDMACH: { Conv_inv_buf_t buf1, buf2; Half ldmach; ldmach = (Half) elfedit_atoconst(argstate.argv[0], ELFEDIT_CONST_EM); /* Set the value */ if (dyn[ndx].d_un.d_val == ldmach) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK), dyn_ndx, dyn_name, EC_WORD(ndx), conv_ehdr_mach(dyn[ndx].d_un.d_val, 0, &buf1)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG), dyn_ndx, dyn_name, EC_WORD(ndx), conv_ehdr_mach(dyn[ndx].d_un.d_val, 0, &buf1), conv_ehdr_mach(ldmach, 0, &buf2)); ret = ELFEDIT_CMDRET_MOD; dyn[ndx].d_un.d_val = ldmach; } } break; } /* * If we modified the dynamic section header, tell libelf. */ if (ret == ELFEDIT_CMDRET_MOD) elfedit_modified_data(argstate.dyn.sec); /* Do autoprint */ if (do_autoprint) print_dyn(cmd, 1, &argstate, print_type, ndx); return (ret); } /* * Command completion functions for the commands */ /* * Command completion for the first argument, which specifies * the dynamic element to use. Examines the options to see if * -dynndx is present, and if not, supplies the completion * strings for argument 1. */ /*ARGSUSED*/ static void cpl_eltarg(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { elfedit_section_t *cache; Dyn *dyn; Word i; const char *s; char *s2; char buf[128]; /* Make sure it's the first argument */ if ((argc - num_opt) != 1) return; /* Is -dynndx present? If so, we don't complete tag types */ for (i = 0; i < num_opt; i++) if (strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_DYNNDX)) == 0) return; /* * If there is no object, or if there is no dynamic section, * then supply all possible names. */ if ((obj_state == NULL) || (obj_state->os_dynndx == SHN_UNDEF)) { elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DT); return; } /* Supply completions for the tags present in the dynamic section */ cache = &obj_state->os_secarr[obj_state->os_dynndx]; dyn = (Dyn *) cache->sec_data->d_buf; i = cache->sec_shdr->sh_size / cache->sec_shdr->sh_entsize; for (; i-- > 0; dyn++) { s = elfedit_atoconst_value_to_str(ELFEDIT_CONST_DT, dyn->d_tag, 0); if (s == NULL) continue; elfedit_cpl_match(cpldata, s, 1); /* * To get the informal tag names that are lowercase * and lack the leading DT_, we copy the string we * have into a buffer and process it. */ if (strlen(s) < 3) continue; (void) strlcpy(buf, s + 3, sizeof (buf)); for (s2 = buf; *s2 != '\0'; s2++) if (isupper(*s2)) *s2 = tolower(*s2); elfedit_cpl_match(cpldata, buf, 1); } } /*ARGSUSED*/ static void cpl_tag(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { /* First argument */ if ((argc - num_opt) == 1) { cpl_eltarg(obj_state, cpldata, argc, argv, num_opt); return; } /* The second argument is always a tag value */ if ((argc - num_opt) == 2) elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DT); } /*ARGSUSED*/ static void cpl_posflag1(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { /* * dyn:posflag1 accepts two mutually exclusive options that have * a corresponding value argument: -dynndx and -needed. If we * are being called to supply options for the value, handle that here. */ if ((num_opt > 1) && (argc == num_opt)) { elfedit_section_t *dynsec, *strsec; const char *opt = argv[num_opt - 2]; dyn_opt_t type; Dyn *dyn; Word i, num; /* * If there is no object available, or if the object has no * dynamic section, then there is nothing to report. */ if ((obj_state == NULL) || obj_state->os_dynndx == SHN_UNDEF) return; /* * Determine which option it is, bail if it isn't one of * the ones we are concerned with. */ if ((strcmp(opt, MSG_ORIG(MSG_STR_MINUS_NEEDED)) == 0)) type = DYN_OPT_F_NEEDED; else if ((strcmp(opt, MSG_ORIG(MSG_STR_MINUS_DYNNDX)) == 0)) type = DYN_OPT_F_DYNNDX_VAL; else return; dynsec = elfedit_sec_getdyn(obj_state, &dyn, &num); switch (type) { case DYN_OPT_F_NEEDED: strsec = elfedit_sec_getstr(obj_state, dynsec->sec_shdr->sh_link, 0); for (; num-- > 0; dyn++) if (dyn->d_tag == DT_NEEDED) elfedit_cpl_match(cpldata, elfedit_offset_to_str(strsec, dyn->d_un.d_val, ELFEDIT_MSG_DEBUG, 0), 0); break; case DYN_OPT_F_DYNNDX_VAL: for (i = 0; i < num; i++, dyn++) if (dyn->d_tag == DT_POSFLAG_1) elfedit_cpl_ndx(cpldata, i); break; } return; } /* This routine allows multiple flags to be specified */ elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DF_P1); } /*ARGSUSED*/ static void cpl_flags(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { /* This routine allows multiple flags to be specified */ elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DF); } /*ARGSUSED*/ static void cpl_flags1(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { /* This routine allows multiple flags to be specified */ elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DF_1); } /*ARGSUSED*/ static void cpl_feature1(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { /* This routine allows multiple flags to be specified */ elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_DTF_1); } /*ARGSUSED*/ static void cpl_sunw_ldmach(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { /* * This command doesn't accept options, so num_opt should be * 0. This is a defensive measure, in case that should change. */ argc -= num_opt; argv += num_opt; if (argc == 1) elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_EM); } /* * 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(DYN_CMD_T_DUMP, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_tag(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_TAG, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_value(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_VALUE, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_delete(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_DELETE, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_move(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_MOVE, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_runpath(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_RUNPATH, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_posflag1(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_POSFLAG1, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_flags(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_FLAGS, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_flags1(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_FLAGS1, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_feature1(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_FEATURE1, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_checksum(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_CHECKSUM, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_sunw_ldmach(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(DYN_CMD_T_SUNW_LDMACH, obj_state, argc, argv)); } /*ARGSUSED*/ elfedit_module_t * elfedit_init(elfedit_module_version_t version) { /* For commands that only accept -o */ static elfedit_cmd_optarg_t opt_ostyle[] = { { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { NULL } }; /* For commands that only accept -and, -cmp, -o, -or */ static elfedit_cmd_optarg_t opt_ostyle_bitop[] = { { ELFEDIT_STDOA_OPT_AND, NULL, ELFEDIT_CMDOA_F_INHERIT, DYN_OPT_F_AND, DYN_OPT_F_OR }, { ELFEDIT_STDOA_OPT_CMP, NULL, ELFEDIT_CMDOA_F_INHERIT, DYN_OPT_F_CMP, 0 }, { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { ELFEDIT_STDOA_OPT_OR, NULL, ELFEDIT_CMDOA_F_INHERIT, DYN_OPT_F_OR, DYN_OPT_F_AND }, { NULL } }; /* For commands that only accept -dynndx */ static elfedit_cmd_optarg_t opt_minus_dynndx[] = { { MSG_ORIG(MSG_STR_MINUS_DYNNDX), /* MSG_INTL(MSG_OPTDESC_DYNNDX_ELT) */ ELFEDIT_I18NHDL(MSG_OPTDESC_DYNNDX_ELT), 0, DYN_OPT_F_DYNNDX_ELT, 0 }, { NULL } }; /* dyn: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 arg_dump[] = { { MSG_ORIG(MSG_STR_ELT), /* MSG_INTL(MSG_ARGDESC_ELT) */ ELFEDIT_I18NHDL(MSG_ARGDESC_ELT), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* dyn:tag */ static const char *name_tag[] = { MSG_ORIG(MSG_CMD_TAG), NULL }; static elfedit_cmd_optarg_t opt_tag[] = { { MSG_ORIG(MSG_STR_MINUS_DYNNDX), /* MSG_INTL(MSG_OPTDESC_DYNNDX_ELT) */ ELFEDIT_I18NHDL(MSG_OPTDESC_DYNNDX_ELT), 0, DYN_OPT_F_DYNNDX_ELT, 0 }, { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { NULL } }; static elfedit_cmd_optarg_t arg_tag[] = { { MSG_ORIG(MSG_STR_ELT), /* MSG_INTL(MSG_A1_TAG_ELT) */ ELFEDIT_I18NHDL(MSG_A1_TAG_ELT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A2_TAG_VALUE) */ ELFEDIT_I18NHDL(MSG_A2_TAG_VALUE), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* dyn:value */ static const char *name_value[] = { MSG_ORIG(MSG_CMD_VALUE), NULL }; static elfedit_cmd_optarg_t opt_value[] = { { MSG_ORIG(MSG_STR_MINUS_ADD), /* MSG_INTL(MSG_OPTDESC_ADD) */ ELFEDIT_I18NHDL(MSG_OPTDESC_ADD), 0, DYN_OPT_F_ADD, DYN_OPT_F_DYNNDX_ELT }, { MSG_ORIG(MSG_STR_MINUS_DYNNDX), /* MSG_INTL(MSG_OPTDESC_DYNNDX_ELT) */ ELFEDIT_I18NHDL(MSG_OPTDESC_DYNNDX_ELT), 0, DYN_OPT_F_DYNNDX_ELT, DYN_OPT_F_ADD }, { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { MSG_ORIG(MSG_STR_MINUS_S), /* MSG_INTL(MSG_OPTDESC_S) */ ELFEDIT_I18NHDL(MSG_OPTDESC_S), 0, DYN_OPT_F_STRVAL, 0 }, { NULL } }; static elfedit_cmd_optarg_t arg_value[] = { { MSG_ORIG(MSG_STR_ELT), /* MSG_INTL(MSG_ARGDESC_ELT) */ ELFEDIT_I18NHDL(MSG_ARGDESC_ELT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A2_VALUE_VALUE) */ ELFEDIT_I18NHDL(MSG_A2_VALUE_VALUE), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* dyn:delete */ static const char *name_delete[] = { MSG_ORIG(MSG_CMD_DELETE), NULL }; static elfedit_cmd_optarg_t arg_delete[] = { { MSG_ORIG(MSG_STR_ELT), /* MSG_INTL(MSG_ARGDESC_ELT) */ ELFEDIT_I18NHDL(MSG_ARGDESC_ELT), 0 }, { MSG_ORIG(MSG_STR_COUNT), /* MSG_INTL(MSG_A2_DELETE_COUNT) */ ELFEDIT_I18NHDL(MSG_A2_DELETE_COUNT), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* dyn:move */ static const char *name_move[] = { MSG_ORIG(MSG_CMD_MOVE), NULL }; static elfedit_cmd_optarg_t arg_move[] = { { MSG_ORIG(MSG_STR_ELT), /* MSG_INTL(MSG_ARGDESC_ELT) */ ELFEDIT_I18NHDL(MSG_ARGDESC_ELT), 0 }, { MSG_ORIG(MSG_STR_DST_INDEX), /* MSG_INTL(MSG_A2_MOVE_DST_INDEX) */ ELFEDIT_I18NHDL(MSG_A2_MOVE_DST_INDEX), 0 }, { MSG_ORIG(MSG_STR_COUNT), /* MSG_INTL(MSG_A3_MOVE_COUNT) */ ELFEDIT_I18NHDL(MSG_A3_MOVE_COUNT), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* dyn:runpath / dyn:rpath */ static const char *name_runpath[] = { MSG_ORIG(MSG_CMD_RUNPATH), MSG_ORIG(MSG_CMD_RUNPATH_A1), NULL }; static elfedit_cmd_optarg_t arg_runpath[] = { { MSG_ORIG(MSG_STR_NEWPATH), /* MSG_INTL(MSG_A1_RUNPATH_NEWPATH) */ ELFEDIT_I18NHDL(MSG_A1_RUNPATH_NEWPATH), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* dyn:posflag1 */ static const char *name_posflag1[] = { MSG_ORIG(MSG_CMD_POSFLAG1), NULL }; static elfedit_cmd_optarg_t opt_posflag1[] = { { ELFEDIT_STDOA_OPT_AND, NULL, ELFEDIT_CMDOA_F_INHERIT, DYN_OPT_F_AND, DYN_OPT_F_OR }, { ELFEDIT_STDOA_OPT_CMP, NULL, ELFEDIT_CMDOA_F_INHERIT, DYN_OPT_F_CMP, 0 }, { MSG_ORIG(MSG_STR_MINUS_DYNNDX), /* MSG_INTL(MSG_OPTDESC_DYNNDX_VAL) */ ELFEDIT_I18NHDL(MSG_OPTDESC_DYNNDX_VAL), ELFEDIT_CMDOA_F_VALUE, DYN_OPT_F_DYNNDX_VAL, DYN_OPT_F_NEEDED }, { MSG_ORIG(MSG_STR_INDEX), NULL, 0, 0 }, { MSG_ORIG(MSG_STR_MINUS_NEEDED), /* MSG_INTL(MSG_OPTDESC_NEEDED) */ ELFEDIT_I18NHDL(MSG_OPTDESC_NEEDED), ELFEDIT_CMDOA_F_VALUE, DYN_OPT_F_NEEDED, DYN_OPT_F_DYNNDX_VAL }, { MSG_ORIG(MSG_STR_PREFIX), NULL, 0, 0 }, { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { ELFEDIT_STDOA_OPT_OR, NULL, ELFEDIT_CMDOA_F_INHERIT, DYN_OPT_F_OR, DYN_OPT_F_AND }, { NULL } }; static elfedit_cmd_optarg_t arg_posflag1[] = { { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A1_POSFLAG1_VALUE) */ ELFEDIT_I18NHDL(MSG_A1_POSFLAG1_VALUE), ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT }, { NULL } }; /* dyn:flags */ static const char *name_flags[] = { MSG_ORIG(MSG_CMD_FLAGS), NULL }; static elfedit_cmd_optarg_t arg_flags[] = { { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A1_FLAGS_VALUE) */ ELFEDIT_I18NHDL(MSG_A1_FLAGS_VALUE), ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT }, { NULL } }; /* dyn:flags1 */ static const char *name_flags1[] = { MSG_ORIG(MSG_CMD_FLAGS1), NULL }; static elfedit_cmd_optarg_t arg_flags1[] = { { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A1_FLAGS1_VALUE) */ ELFEDIT_I18NHDL(MSG_A1_FLAGS1_VALUE), ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT }, { NULL } }; /* dyn:feature1 */ static const char *name_feature1[] = { MSG_ORIG(MSG_CMD_FEATURE1), NULL }; static elfedit_cmd_optarg_t arg_feature1[] = { { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A1_FEATURE1_VALUE) */ ELFEDIT_I18NHDL(MSG_A1_FEATURE1_VALUE), ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT }, { NULL } }; /* dyn:checksum */ static const char *name_checksum[] = { MSG_ORIG(MSG_CMD_CHECKSUM), NULL }; /* dyn:sunw_ldmach */ static const char *name_sunw_ldmach[] = { MSG_ORIG(MSG_CMD_SUNW_LDMACH), NULL }; static elfedit_cmd_optarg_t arg_sunw_ldmach[] = { { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A1_SUNW_LDMACH_VALUE) */ ELFEDIT_I18NHDL(MSG_A1_SUNW_LDMACH_VALUE), ELFEDIT_CMDOA_F_OPT }, { NULL } }; static elfedit_cmd_t cmds[] = { /* dyn:dump */ { cmd_dump, cpl_eltarg, name_dump, /* MSG_INTL(MSG_DESC_DUMP) */ ELFEDIT_I18NHDL(MSG_DESC_DUMP), /* MSG_INTL(MSG_HELP_DUMP) */ ELFEDIT_I18NHDL(MSG_HELP_DUMP), opt_minus_dynndx, arg_dump }, /* dyn:tag */ { cmd_tag, cpl_tag, name_tag, /* MSG_INTL(MSG_DESC_TAG) */ ELFEDIT_I18NHDL(MSG_DESC_TAG), /* MSG_INTL(MSG_HELP_TAG) */ ELFEDIT_I18NHDL(MSG_HELP_TAG), opt_tag, arg_tag }, /* dyn:value */ { cmd_value, cpl_eltarg, name_value, /* MSG_INTL(MSG_DESC_VALUE) */ ELFEDIT_I18NHDL(MSG_DESC_VALUE), /* MSG_INTL(MSG_HELP_VALUE) */ ELFEDIT_I18NHDL(MSG_HELP_VALUE), opt_value, arg_value }, /* dyn:delete */ { cmd_delete, cpl_eltarg, name_delete, /* MSG_INTL(MSG_DESC_DELETE) */ ELFEDIT_I18NHDL(MSG_DESC_DELETE), /* MSG_INTL(MSG_HELP_DELETE) */ ELFEDIT_I18NHDL(MSG_HELP_DELETE), opt_minus_dynndx, arg_delete }, /* dyn:move */ { cmd_move, cpl_eltarg, name_move, /* MSG_INTL(MSG_DESC_MOVE) */ ELFEDIT_I18NHDL(MSG_DESC_MOVE), /* MSG_INTL(MSG_HELP_MOVE) */ ELFEDIT_I18NHDL(MSG_HELP_MOVE), opt_minus_dynndx, arg_move }, /* dyn:runpath */ { cmd_runpath, NULL, name_runpath, /* MSG_INTL(MSG_DESC_RUNPATH) */ ELFEDIT_I18NHDL(MSG_DESC_RUNPATH), /* MSG_INTL(MSG_HELP_RUNPATH) */ ELFEDIT_I18NHDL(MSG_HELP_RUNPATH), opt_ostyle, arg_runpath }, /* dyn:posflag1 */ { cmd_posflag1, cpl_posflag1, name_posflag1, /* MSG_INTL(MSG_DESC_POSFLAG1) */ ELFEDIT_I18NHDL(MSG_DESC_POSFLAG1), /* MSG_INTL(MSG_HELP_POSFLAG1) */ ELFEDIT_I18NHDL(MSG_HELP_POSFLAG1), opt_posflag1, arg_posflag1 }, /* dyn:flags */ { cmd_flags, cpl_flags, name_flags, /* MSG_INTL(MSG_DESC_FLAGS) */ ELFEDIT_I18NHDL(MSG_DESC_FLAGS), /* MSG_INTL(MSG_HELP_FLAGS) */ ELFEDIT_I18NHDL(MSG_HELP_FLAGS), opt_ostyle_bitop, arg_flags }, /* dyn:flags1 */ { cmd_flags1, cpl_flags1, name_flags1, /* MSG_INTL(MSG_DESC_FLAGS1) */ ELFEDIT_I18NHDL(MSG_DESC_FLAGS1), /* MSG_INTL(MSG_HELP_FLAGS1) */ ELFEDIT_I18NHDL(MSG_HELP_FLAGS1), opt_ostyle_bitop, arg_flags1 }, /* dyn:feature1 */ { cmd_feature1, cpl_feature1, name_feature1, /* MSG_INTL(MSG_DESC_FEATURE1) */ ELFEDIT_I18NHDL(MSG_DESC_FEATURE1), /* MSG_INTL(MSG_HELP_FEATURE1) */ ELFEDIT_I18NHDL(MSG_HELP_FEATURE1), opt_ostyle_bitop, arg_feature1 }, /* dyn:checksum */ { cmd_checksum, NULL, name_checksum, /* MSG_INTL(MSG_DESC_CHECKSUM) */ ELFEDIT_I18NHDL(MSG_DESC_CHECKSUM), /* MSG_INTL(MSG_HELP_CHECKSUM) */ ELFEDIT_I18NHDL(MSG_HELP_CHECKSUM), NULL, NULL }, /* dyn:sunw_ldmach */ { cmd_sunw_ldmach, cpl_sunw_ldmach, name_sunw_ldmach, /* MSG_INTL(MSG_DESC_SUNW_LDMACH) */ ELFEDIT_I18NHDL(MSG_DESC_SUNW_LDMACH), /* MSG_INTL(MSG_HELP_SUNW_LDMACH) */ ELFEDIT_I18NHDL(MSG_HELP_SUNW_LDMACH), opt_ostyle, arg_sunw_ldmach }, { 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); }