/* * 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 /* * Program headers */ /* * 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 */ PHDR_CMD_T_DUMP = 0, /* phdr:dump */ /* Commands that correspond directly to program header fields */ PHDR_CMD_T_P_TYPE = 1, /* phdr:p_type */ PHDR_CMD_T_P_OFFSET = 2, /* phdr:p_offset */ PHDR_CMD_T_P_VADDR = 3, /* phdr:p_vaddr */ PHDR_CMD_T_P_PADDR = 4, /* phdr:p_paddr */ PHDR_CMD_T_P_FILESZ = 5, /* phdr:p_filesz */ PHDR_CMD_T_P_MEMSZ = 6, /* phdr:p_memsz */ PHDR_CMD_T_P_FLAGS = 7, /* phdr:p_flags */ PHDR_CMD_T_P_ALIGN = 8, /* phdr:p_align */ /* Commands that do not correspond directly to a specific phdr tag */ PHDR_CMD_T_INTERP = 9, /* phdr:interp */ PHDR_CMD_T_DELETE = 10, /* phdr:delete */ PHDR_CMD_T_MOVE = 11 /* phdr:move */ } PHDR_CMD_T; /* * The following type is ued by locate_interp() to return * information about the interpreter program header. */ typedef struct { Word phndx; /* Index of PT_INTERP header */ Phdr *phdr; /* PT_INTERP header */ elfedit_section_t *sec; /* Section containing string */ Word stroff; /* Offset into string section */ const char *str; /* Interpreter string */ } INTERP_STATE; #ifndef _ELF64 /* * We supply this function for the msg module */ const char * _phdr_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 phdr_opt_t enum specifies a bit value for every optional * argument allowed by a command in this module. */ typedef enum { PHDR_OPT_F_AND = 1, /* -and: AND (&) values to dest */ PHDR_OPT_F_CMP = 2, /* -cmp: Complement (~) values */ PHDR_OPT_F_PHNDX = 4, /* -phndx: Program header by index, */ /* not by name */ PHDR_OPT_F_OR = 8 /* -or: OR (|) values to dest */ } phdr_opt_t; /* * A variable of type ARGSTATE is used by each command to maintain * information about the section headers and related things. It is * initialized by process_args(), and used by the other routines. */ typedef struct { elfedit_obj_state_t *obj_state; phdr_opt_t optmask; /* Mask of options used */ int argc; /* # of plain arguments */ const char **argv; /* Plain arguments */ int ndx_set; /* True if ndx is valid */ Word ndx; /* Index of header if cmd */ /* accepts it */ int print_req; /* Call is a print request */ } ARGSTATE; /* * Standard argument processing for phdr module * * entry * obj_state, argc, argv - Standard command arguments * optmask - Mask of allowed optional arguments. * cmd - PHDR_CMD_T_* value giving identify of caller * 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[], PHDR_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; /* Are the right number of plain arguments present? */ switch (cmd) { case PHDR_CMD_T_DUMP: if (argc > 1) elfedit_command_usage(); argstate->print_req = 1; break; case PHDR_CMD_T_P_FLAGS: /* phdr:sh_flags allows an arbitrary number of arguments */ argstate->print_req = (argc < 2); break; case PHDR_CMD_T_INTERP: if (argc > 1) elfedit_command_usage(); argstate->print_req = (argc == 0); break; case PHDR_CMD_T_DELETE: if ((argc < 1) || (argc > 2)) elfedit_command_usage(); argstate->print_req = 0; break; case PHDR_CMD_T_MOVE: if ((argc < 2) || (argc > 3)) elfedit_command_usage(); argstate->print_req = 0; break; default: /* The remaining commands accept 2 plain arguments */ if (argc > 2) elfedit_command_usage(); argstate->print_req = (argc < 2); break; } /* Return the updated values of argc/argv */ argstate->argc = argc; argstate->argv = argv; argstate->ndx_set = 0; if ((argc > 0) && (cmd != PHDR_CMD_T_INTERP)) { /* * If the -phndx option is present, the first argument is * the index of the header to use. Otherwise, it is a * name corresponding to its type, similar to the way * elfdump works with its -N option. */ if (argstate->optmask & PHDR_OPT_F_PHNDX) { argstate->ndx = (Word) elfedit_atoui_range( argstate->argv[0], MSG_ORIG(MSG_STR_ELEMENT), 0, argstate->obj_state->os_phnum - 1, NULL); argstate->ndx_set = 1; } else { Conv_inv_buf_t inv_buf; Word i; Phdr *phdr; argstate->ndx = (Word) elfedit_atoconst( argstate->argv[0], ELFEDIT_CONST_PT); phdr = obj_state->os_phdr; for (i = 0; i < obj_state->os_phnum; i++, phdr++) { if (phdr->p_type == argstate->ndx) { argstate->ndx = i; argstate->ndx_set = 1; elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_PHDR), EC_WORD(i), conv_phdr_type( obj_state->os_ehdr->e_machine, phdr->p_type, 0, &inv_buf)); break; } } if (i == argstate->obj_state->os_phnum) elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOPHDR), conv_phdr_type( obj_state->os_ehdr->e_machine, argstate->ndx, 0, &inv_buf)); } } /* If there may be an arbitrary amount of output, use a pager */ if (argc == 0) elfedit_pager_init(); } /* * Locate the interpreter string for the object and related information * * entry: * obj_state - Object state * interp - NULL, or variable to be filled in with information * about the interpteter string. */ static const char * locate_interp(elfedit_obj_state_t *obj_state, INTERP_STATE *interp) { INTERP_STATE local_interp; elfedit_section_t *strsec; /* String table */ size_t phnum; /* # of program headers */ int phndx; /* Index of PT_INTERP program header */ Phdr *phdr; /* Program header array */ Word i; if (interp == NULL) interp = &local_interp; /* Locate the PT_INTERP program header */ phnum = obj_state->os_phnum; phdr = obj_state->os_phdr; for (phndx = 0; phndx < phnum; phndx++) { if (phdr[phndx].p_type == PT_INTERP) { interp->phndx = phndx; interp->phdr = phdr + phndx; break; } } /* If no PT_INTERP program header found, we cannot proceed */ if (phndx == phnum) elfedit_elferr(obj_state->os_file, MSG_INTL(MSG_ERR_NOINTERPPHDR)); /* * Locate the section containing the interpteter string as well * as the string itself. * * The program header contains a direct offset to the string, so * we find the section by walking through the them looking for * the one with a base and size that would contain the string. * Note that this target section cannot be in a NOBITS section. */ for (i = 1; i < obj_state->os_shnum; i++) { strsec = &obj_state->os_secarr[i]; if ((strsec->sec_shdr->sh_type != SHT_NOBITS) && (interp->phdr->p_offset >= strsec->sec_shdr->sh_offset) && ((interp->phdr->p_offset + interp->phdr->p_filesz) <= (strsec->sec_shdr->sh_offset + strsec->sec_shdr->sh_size))) { interp->sec = strsec; interp->stroff = interp->phdr->p_offset - strsec->sec_shdr->sh_offset; interp->str = ((char *)strsec->sec_data->d_buf) + interp->stroff; return (interp->str); } } /* * We don't expect to get here: If there is a PT_INTERP header, * we fully expect the string to exist. */ elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOINTERPSEC)); /*NOTREACHED*/ return (NULL); /* For lint */ } /* * Print program header values, taking the calling command, and output style * into account. * * entry: * autoprint - If True, output is only produced if the elfedit * autoprint flag is set. If False, output is always produced. * cmd - PHDR_CMD_T_* value giving identify of caller * argstate - State block for section header array * ndx - Index of first program header to display * cnt - Number of program headers to display */ static void print_phdr(PHDR_CMD_T cmd, int autoprint, ARGSTATE *argstate) { elfedit_outstyle_t outstyle; Word ndx, cnt; if (autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0)) return; if (argstate->ndx_set) { ndx = argstate->ndx; cnt = 1; } else { ndx = 0; cnt = argstate->obj_state->os_phnum; } /* * Pick an output style. phdr:dump is required to use the default * style. The other commands use the current output style. */ outstyle = (cmd == PHDR_CMD_T_DUMP) ? ELFEDIT_OUTSTYLE_DEFAULT : elfedit_outstyle(); /* * If doing default output, use elfdump style where we * show all program header attributes. In this case, the * command that called us doesn't matter. * * Let PHDR_CMD_T_INTERP fall through: It isn't per-phdr like * the other commands. */ if ((outstyle == ELFEDIT_OUTSTYLE_DEFAULT) && (cmd != PHDR_CMD_T_INTERP)) { Half mach = argstate->obj_state->os_ehdr->e_machine; Phdr *phdr = argstate->obj_state->os_phdr + ndx; for (; cnt--; ndx++, phdr++) { elfedit_printf(MSG_ORIG(MSG_STR_NL)); elfedit_printf(MSG_INTL(MSG_ELF_PHDR), EC_WORD(ndx)); Elf_phdr(0, mach, phdr); } return; } switch (cmd) { case PHDR_CMD_T_P_TYPE: for (; cnt--; ndx++) { Word p_type = argstate->obj_state->os_phdr[ndx].p_type; Conv_inv_buf_t inv_buf; if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) { Half mach = argstate->obj_state->os_ehdr->e_machine; elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), conv_phdr_type(mach, p_type, 0, &inv_buf)); } else { elfedit_printf(MSG_ORIG(MSG_FMT_X_NL), EC_WORD(p_type)); } } return; case PHDR_CMD_T_P_OFFSET: for (; cnt--; ndx++) elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL), EC_OFF(argstate->obj_state->os_phdr[ndx].p_offset)); return; case PHDR_CMD_T_P_VADDR: for (; cnt--; ndx++) elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL), EC_ADDR(argstate->obj_state->os_phdr[ndx].p_vaddr)); return; case PHDR_CMD_T_P_PADDR: for (; cnt--; ndx++) elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL), EC_ADDR(argstate->obj_state->os_phdr[ndx].p_paddr)); return; case PHDR_CMD_T_P_FILESZ: for (; cnt--; ndx++) elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL), EC_XWORD(argstate->obj_state-> os_phdr[ndx].p_filesz)); return; case PHDR_CMD_T_P_MEMSZ: for (; cnt--; ndx++) elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL), EC_XWORD(argstate->obj_state-> os_phdr[ndx].p_memsz)); return; case PHDR_CMD_T_P_FLAGS: for (; cnt--; ndx++) { Word p_flags = argstate->obj_state->os_phdr[ndx].p_flags; if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) { Conv_phdr_flags_buf_t phdr_flags_buf; elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), conv_phdr_flags(p_flags, CONV_FMT_NOBKT, &phdr_flags_buf)); } else { elfedit_printf(MSG_ORIG(MSG_FMT_X_NL), EC_WORD(p_flags)); } } return; case PHDR_CMD_T_P_ALIGN: for (; cnt--; ndx++) elfedit_printf(MSG_ORIG(MSG_FMT_LLX_NL), EC_XWORD(argstate->obj_state-> os_phdr[ndx].p_align)); return; case PHDR_CMD_T_INTERP: { INTERP_STATE interp; (void) locate_interp(argstate->obj_state, &interp); switch (outstyle) { case ELFEDIT_OUTSTYLE_DEFAULT: elfedit_printf(MSG_INTL(MSG_FMT_ELF_INTERP), interp.sec->sec_name, interp.str); break; case ELFEDIT_OUTSTYLE_SIMPLE: elfedit_printf(MSG_ORIG(MSG_FMT_STRNL), interp.str); break; case ELFEDIT_OUTSTYLE_NUM: elfedit_printf(MSG_ORIG(MSG_FMT_U_NL), EC_WORD(interp.stroff)); break; } } return; } } /* * Called from cmd_body() in the case where a plain argument * is given to phdr:interp to change the interpreter. */ static elfedit_cmdret_t cmd_body_set_interp(ARGSTATE *argstate) { elfedit_obj_state_t *obj_state = argstate->obj_state; elfedit_section_t *strsec; /* String table */ INTERP_STATE interp; Word numdyn; /* # of elements in dyn arr */ size_t phnum; /* # of program headers */ Phdr *phdr; /* Program header array */ Word i, j; Word str_offset; /* Offset in strsec to new interp str */ int str_found = 0; /* True when we have new interp str */ Word str_size; /* Size of new interp string + NULL */ phnum = obj_state->os_phnum; phdr = obj_state->os_phdr; /* Locate the PT_INTERP program header */ (void) locate_interp(obj_state, &interp); strsec = interp.sec; str_offset = interp.stroff; /* * If the given string is the same as the existing interpreter * string, say so and return. */ if (strcmp(interp.str, argstate->argv[0]) == 0) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_OLDINTERPOK), EC_WORD(strsec->sec_shndx), strsec->sec_name, EC_WORD(str_offset), interp.str); return (ELFEDIT_CMDRET_NONE); } /* * An ELF PT_INTERP usually references its own special section * instead of some other string table. The ELF ABI says that this * section must be named ".interp". Hence, this is a rare case * in which the name of a section can be taken as an indication * of its contents. .interp is typically sized to just fit * the original string, including its NULL termination. You can * treat it as a string table with one string. * * Thanks to 'elfedit', it may be that we encounter a file where * PT_INTERP does not reference the .interp section. This will happen * if elfedit is used to change the interpreter to a string that is * too big to fit in .interp, in which case we will use the * .dynstr string table (That code is below, in this function). * * Given the above facts, our next step is to locate the .interp * section and see if our new string will fit in it. Since we can't * depend on PT_INTERP, we search the section headers to find a * section whith the following characteristics: * - The name is ".interp". * - Section is allocable (SHF_ALLOC) and SHT_PROGBITS. * - It is not part of a writable segment. * If we find such a section, and the new string fits, we will * write it there. */ str_size = strlen(argstate->argv[0]) + 1; for (i = 1; i < obj_state->os_shnum; i++) { strsec = &obj_state->os_secarr[i]; if ((strcmp(strsec->sec_name, MSG_ORIG(MSG_SEC_INTERP)) == 0) && (strsec->sec_shdr->sh_flags & SHF_ALLOC) && (strsec->sec_shdr->sh_type & SHT_PROGBITS)) { for (j = 0; j < phnum; j++) { Phdr *tphdr = &phdr[j]; if ((strsec->sec_shdr->sh_offset >= tphdr->p_offset) && ((strsec->sec_shdr->sh_offset + strsec->sec_shdr->sh_size) <= (tphdr->p_offset + tphdr->p_filesz)) && (tphdr->p_flags & PF_W)) { break; } } if ((j == phnum) && (str_size <= strsec->sec_shdr->sh_size)) { /* .interp section found, and has room */ str_found = 1; str_offset = 0; elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NEWISTR), EC_WORD(j), strsec->sec_name, EC_WORD(str_offset), argstate->argv[0]); /* Put new value in section */ (void) strncpy((char *)strsec->sec_data->d_buf, argstate->argv[0], strsec->sec_shdr->sh_size); /* Set libelf dirty bit so change is flushed */ elfedit_modified_data(strsec); break; } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LNGISTR), EC_WORD(j), strsec->sec_name, EC_WORD(str_offset), EC_WORD(str_size), EC_WORD(strsec->sec_shdr->sh_size), argstate->argv[0]); } } } /* * If the above did not find a string within the .interp section, * then we have a second option. If this ELF object has a dynamic * section, then we are willing to use strings from within the * associated .dynstr string table. And if there is reserved space * in .dynstr (as reported by the DT_SUNW_STRPAD dynamic entry), * then we are even willing to add a new string to .dynstr. */ if (!str_found) { elfedit_section_t *dynsec; Dyn *dyn; dynsec = elfedit_sec_getdyn(obj_state, &dyn, &numdyn); strsec = elfedit_sec_getstr(obj_state, dynsec->sec_shdr->sh_link, 0); /* Does string exist in the table already, or can we add it? */ str_offset = elfedit_strtab_insert(obj_state, strsec, dynsec, argstate->argv[0]); } /* * If we are here, we know we have a replacement string, because * the errors from checking .dynamic/.dynstr will not allow * things to get here otherwise. * * The PT_INTERP program header references the string directly, * so we add the section offset to the string offset. */ interp.phdr->p_offset = strsec->sec_shdr->sh_offset + str_offset; interp.phdr->p_filesz = str_size; elfedit_modified_phdr(obj_state); elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_SETPHINTERP), EC_WORD(interp.phndx), EC_XWORD(interp.phdr->p_offset), EC_XWORD(interp.phdr->p_filesz)); return (ELFEDIT_CMDRET_MOD); } /* * Common body for the phdr: 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 PHDR_CMD_T_* constants listed above, specifying * which command to implement. * obj_state, argc, argv - Standard command arguments */ static elfedit_cmdret_t cmd_body(PHDR_CMD_T cmd, elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { ARGSTATE argstate; Phdr *phdr; elfedit_cmdret_t ret = ELFEDIT_CMDRET_NONE; int do_autoprint = 1; process_args(obj_state, argc, argv, cmd, &argstate); /* If this is a printing request, print and return */ if (argstate.print_req) { print_phdr(cmd, 0, &argstate); return (ELFEDIT_CMDRET_NONE); } if (argstate.ndx_set) phdr = &argstate.obj_state->os_phdr[argstate.ndx]; switch (cmd) { /* * PHDR_CMD_T_DUMP can't get here: It never has more than * one argument, and is handled above. */ case PHDR_CMD_T_P_TYPE: { Half mach = obj_state->os_ehdr->e_machine; Word p_type = elfedit_atoconst(argstate.argv[1], ELFEDIT_CONST_PT); Conv_inv_buf_t inv_buf1, inv_buf2; if (phdr->p_type == p_type) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK), argstate.ndx, MSG_ORIG(MSG_CMD_P_TYPE), conv_phdr_type(mach, phdr->p_type, 0, &inv_buf1)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG), argstate.ndx, MSG_ORIG(MSG_CMD_P_TYPE), conv_phdr_type(mach, phdr->p_type, 0, &inv_buf1), conv_phdr_type(mach, p_type, 0, &inv_buf2)); ret = ELFEDIT_CMDRET_MOD; phdr->p_type = p_type; } } break; case PHDR_CMD_T_P_OFFSET: { Off p_offset; p_offset = elfedit_atoui(argstate.argv[1], NULL); if (phdr->p_offset == p_offset) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_OK), argstate.ndx, MSG_ORIG(MSG_CMD_P_OFFSET), EC_XWORD(phdr->p_offset)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_CHG), argstate.ndx, MSG_ORIG(MSG_CMD_P_OFFSET), EC_XWORD(phdr->p_offset), EC_XWORD(p_offset)); ret = ELFEDIT_CMDRET_MOD; phdr->p_offset = p_offset; } } break; case PHDR_CMD_T_P_VADDR: { Addr p_vaddr = elfedit_atoui(argstate.argv[1], NULL); if (phdr->p_vaddr == p_vaddr) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_OK), argstate.ndx, MSG_ORIG(MSG_CMD_P_VADDR), EC_ADDR(phdr->p_vaddr)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_CHG), argstate.ndx, MSG_ORIG(MSG_CMD_P_VADDR), EC_ADDR(phdr->p_vaddr), EC_ADDR(p_vaddr)); ret = ELFEDIT_CMDRET_MOD; phdr->p_vaddr = p_vaddr; } } break; case PHDR_CMD_T_P_PADDR: { Addr p_paddr = elfedit_atoui(argstate.argv[1], NULL); if (phdr->p_paddr == p_paddr) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_OK), argstate.ndx, MSG_ORIG(MSG_CMD_P_PADDR), EC_ADDR(phdr->p_paddr)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_CHG), argstate.ndx, MSG_ORIG(MSG_CMD_P_PADDR), EC_ADDR(phdr->p_paddr), EC_ADDR(p_paddr)); ret = ELFEDIT_CMDRET_MOD; phdr->p_paddr = p_paddr; } } break; case PHDR_CMD_T_P_FILESZ: { Xword p_filesz = elfedit_atoui(argstate.argv[1], NULL); if (phdr->p_filesz == p_filesz) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_OK), argstate.ndx, MSG_ORIG(MSG_CMD_P_FILESZ), EC_XWORD(phdr->p_filesz)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_CHG), argstate.ndx, MSG_ORIG(MSG_CMD_P_FILESZ), EC_XWORD(phdr->p_filesz), EC_XWORD(p_filesz)); ret = ELFEDIT_CMDRET_MOD; phdr->p_filesz = p_filesz; } } break; case PHDR_CMD_T_P_MEMSZ: { Xword p_memsz = elfedit_atoui(argstate.argv[1], NULL); if (phdr->p_memsz == p_memsz) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_OK), argstate.ndx, MSG_ORIG(MSG_CMD_P_MEMSZ), EC_XWORD(phdr->p_memsz)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_CHG), argstate.ndx, MSG_ORIG(MSG_CMD_P_MEMSZ), EC_XWORD(phdr->p_memsz), EC_XWORD(p_memsz)); ret = ELFEDIT_CMDRET_MOD; phdr->p_memsz = p_memsz; } } break; case PHDR_CMD_T_P_FLAGS: { Conv_phdr_flags_buf_t buf1, buf2; Word p_flags = 0; int i; /* Collect the flag arguments */ for (i = 1; i < argstate.argc; i++) p_flags |= (Word) elfedit_atoconst(argstate.argv[i], ELFEDIT_CONST_PF); /* Complement the value? */ if (argstate.optmask & PHDR_OPT_F_CMP) p_flags = ~p_flags; /* Perform any requested bit operations */ if (argstate.optmask & PHDR_OPT_F_AND) p_flags &= phdr->p_flags; else if (argstate.optmask & PHDR_OPT_F_OR) p_flags |= phdr->p_flags; /* Set the value */ if (phdr->p_flags == p_flags) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_OK), argstate.ndx, MSG_ORIG(MSG_CMD_P_FLAGS), conv_phdr_flags(phdr->p_flags, 0, &buf1)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_S_CHG), argstate.ndx, MSG_ORIG(MSG_CMD_P_FLAGS), conv_phdr_flags(phdr->p_flags, 0, &buf1), conv_phdr_flags(p_flags, 0, &buf2)); ret = ELFEDIT_CMDRET_MOD; phdr->p_flags = p_flags; } } break; case PHDR_CMD_T_P_ALIGN: { Xword p_align = elfedit_atoui(argstate.argv[1], NULL); if (phdr->p_align == p_align) { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_OK), argstate.ndx, MSG_ORIG(MSG_CMD_P_ALIGN), EC_XWORD(phdr->p_align)); } else { elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_LLX_CHG), argstate.ndx, MSG_ORIG(MSG_CMD_P_ALIGN), EC_XWORD(phdr->p_align), EC_XWORD(p_align)); ret = ELFEDIT_CMDRET_MOD; phdr->p_align = p_align; } } break; case PHDR_CMD_T_INTERP: ret = cmd_body_set_interp(&argstate); break; case PHDR_CMD_T_DELETE: { Word cnt = (argstate.argc == 1) ? 1 : (Word) elfedit_atoui_range(argstate.argv[1], MSG_ORIG(MSG_STR_COUNT), 1, obj_state->os_phnum - argstate.ndx, NULL); elfedit_array_elts_delete(MSG_ORIG(MSG_MOD_NAME), obj_state->os_phdr, sizeof (Phdr), obj_state->os_phnum, argstate.ndx, cnt); do_autoprint = 0; ret = ELFEDIT_CMDRET_MOD; } break; case PHDR_CMD_T_MOVE: { Phdr save; Word cnt; Word dstndx; do_autoprint = 0; dstndx = (Word) elfedit_atoui_range(argstate.argv[1], MSG_ORIG(MSG_STR_DST_INDEX), 0, obj_state->os_phnum - 1, NULL); if (argstate.argc == 2) { cnt = 1; } else { cnt = (Word) elfedit_atoui_range( argstate.argv[2], MSG_ORIG(MSG_STR_COUNT), 1, obj_state->os_phnum, NULL); } elfedit_array_elts_move(MSG_ORIG(MSG_MOD_NAME), obj_state->os_phdr, sizeof (save), obj_state->os_phnum, argstate.ndx, dstndx, cnt, &save); ret = ELFEDIT_CMDRET_MOD; } break; } /* * If we modified the section header array, tell libelf. */ if (ret == ELFEDIT_CMDRET_MOD) elfedit_modified_phdr(obj_state); /* Do autoprint */ if (do_autoprint) print_phdr(cmd, 1, &argstate); return (ret); } /* * Command completion functions for the various commands */ /* * A number of the commands accept a PT_ constant as their first * argument as long as the -phndx option is not used. */ /*ARGSUSED*/ static void cpl_1starg_pt(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { int i; for (i = 0; i < num_opt; i++) if (strcmp(MSG_ORIG(MSG_STR_MINUS_PHNDX), argv[i]) == 0) return; if (argc == (num_opt + 1)) elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_PT); } /*ARGSUSED*/ static void cpl_p_type(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { /* The first argument follows the standard rules */ cpl_1starg_pt(obj_state, cpldata, argc, argv, num_opt); /* The second argument can be a PT_ value */ if (argc == (num_opt + 2)) elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_PT); } /*ARGSUSED*/ static void cpl_p_flags(elfedit_obj_state_t *obj_state, void *cpldata, int argc, const char *argv[], int num_opt) { /* The first argument follows the standard rules */ cpl_1starg_pt(obj_state, cpldata, argc, argv, num_opt); /* The second and following arguments can be an PF_ value */ if (argc >= (num_opt + 2)) elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_PF); } /* * 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(PHDR_CMD_T_DUMP, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_p_type(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_CMD_T_P_TYPE, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_p_offset(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_CMD_T_P_OFFSET, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_p_vaddr(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_CMD_T_P_VADDR, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_p_paddr(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_CMD_T_P_PADDR, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_p_filesz(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_CMD_T_P_FILESZ, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_p_memsz(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_CMD_T_P_MEMSZ, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_p_flags(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_CMD_T_P_FLAGS, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_p_align(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_CMD_T_P_ALIGN, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_interp(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_CMD_T_INTERP, obj_state, argc, argv)); } static elfedit_cmdret_t cmd_delete(elfedit_obj_state_t *obj_state, int argc, const char *argv[]) { return (cmd_body(PHDR_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(PHDR_CMD_T_MOVE, obj_state, argc, argv)); } /*ARGSUSED*/ elfedit_module_t * elfedit_init(elfedit_module_version_t version) { /* Multiple commands accept a standard set of options */ static elfedit_cmd_optarg_t opt_std[] = { { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { MSG_ORIG(MSG_STR_MINUS_PHNDX), /* MSG_INTL(MSG_OPTDESC_PHNDX) */ ELFEDIT_I18NHDL(MSG_OPTDESC_PHNDX), 0, PHDR_OPT_F_PHNDX, 0 }, { NULL } }; /* For commands that only accept -phndx */ static elfedit_cmd_optarg_t opt_minus_phndx[] = { { MSG_ORIG(MSG_STR_MINUS_PHNDX), /* MSG_INTL(MSG_OPTDESC_PHNDX) */ ELFEDIT_I18NHDL(MSG_OPTDESC_PHNDX), 0, PHDR_OPT_F_PHNDX, 0 }, { NULL } }; /* phdr: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_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* phdr:p_type */ static const char *name_p_type[] = { MSG_ORIG(MSG_CMD_P_TYPE), NULL }; static elfedit_cmd_optarg_t arg_p_type[] = { { MSG_ORIG(MSG_STR_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_TYPE), /* MSG_INTL(MSG_A2_P_TYPE_TYPE) */ ELFEDIT_I18NHDL(MSG_A2_P_TYPE_TYPE), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* phdr:p_offset */ static const char *name_p_offset[] = { MSG_ORIG(MSG_CMD_P_OFFSET), NULL }; static elfedit_cmd_optarg_t arg_p_offset[] = { { MSG_ORIG(MSG_STR_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A2_P_OFFSET_VALUE) */ ELFEDIT_I18NHDL(MSG_A2_P_OFFSET_VALUE), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* phdr:p_vaddr */ static const char *name_p_vaddr[] = { MSG_ORIG(MSG_CMD_P_VADDR), NULL }; static elfedit_cmd_optarg_t arg_p_vaddr[] = { { MSG_ORIG(MSG_STR_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_ADDR), /* MSG_INTL(MSG_A2_P_VADDR_ADDR) */ ELFEDIT_I18NHDL(MSG_A2_P_VADDR_ADDR), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* phdr:p_paddr */ static const char *name_p_paddr[] = { MSG_ORIG(MSG_CMD_P_PADDR), NULL }; static elfedit_cmd_optarg_t arg_p_paddr[] = { { MSG_ORIG(MSG_STR_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_ADDR), /* MSG_INTL(MSG_A2_P_PADDR_ADDR) */ ELFEDIT_I18NHDL(MSG_A2_P_PADDR_ADDR), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* phdr:p_filesz */ static const char *name_p_filesz[] = { MSG_ORIG(MSG_CMD_P_FILESZ), NULL }; static elfedit_cmd_optarg_t arg_p_filesz[] = { /* MSG_INTL(MSG_A1_ELEMENT) */ { MSG_ORIG(MSG_STR_ELEMENT), ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_SIZE), /* MSG_INTL(MSG_A2_P_FILESZ_SIZE) */ ELFEDIT_I18NHDL(MSG_A2_P_FILESZ_SIZE), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* phdr:p_memsz */ static const char *name_p_memsz[] = { MSG_ORIG(MSG_CMD_P_MEMSZ), NULL }; static elfedit_cmd_optarg_t arg_p_memsz[] = { { MSG_ORIG(MSG_STR_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_SIZE), /* MSG_INTL(MSG_A2_P_MEMSZ_SIZE) */ ELFEDIT_I18NHDL(MSG_A2_P_MEMSZ_SIZE), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* shdr:p_flags */ static const char *name_p_flags[] = { MSG_ORIG(MSG_CMD_P_FLAGS), NULL }; static elfedit_cmd_optarg_t opt_p_flags[] = { { ELFEDIT_STDOA_OPT_AND, NULL, ELFEDIT_CMDOA_F_INHERIT, PHDR_OPT_F_AND, PHDR_OPT_F_OR }, { ELFEDIT_STDOA_OPT_CMP, NULL, ELFEDIT_CMDOA_F_INHERIT, PHDR_OPT_F_CMP, 0 }, { MSG_ORIG(MSG_STR_MINUS_PHNDX), /* MSG_INTL(MSG_OPTDESC_PHNDX) */ ELFEDIT_I18NHDL(MSG_OPTDESC_PHNDX), 0, PHDR_OPT_F_PHNDX, 0 }, { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { ELFEDIT_STDOA_OPT_OR, NULL, ELFEDIT_CMDOA_F_INHERIT, PHDR_OPT_F_OR, PHDR_OPT_F_AND }, { NULL } }; static elfedit_cmd_optarg_t arg_p_flags[] = { { MSG_ORIG(MSG_STR_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_VALUE), /* MSG_INTL(MSG_A2_P_FLAGS_VALUE) */ ELFEDIT_I18NHDL(MSG_A2_P_FLAGS_VALUE), ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT }, { NULL } }; /* phdr:p_align */ static const char *name_p_align[] = { MSG_ORIG(MSG_CMD_P_ALIGN), NULL }; static elfedit_cmd_optarg_t arg_p_align[] = { { MSG_ORIG(MSG_STR_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_ALIGN), /* MSG_INTL(MSG_A2_P_ALIGN_ALIGN) */ ELFEDIT_I18NHDL(MSG_A2_P_ALIGN_ALIGN), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* phdr:interp */ static const char *name_interp[] = { MSG_ORIG(MSG_CMD_INTERP), NULL }; static elfedit_cmd_optarg_t opt_interp[] = { { ELFEDIT_STDOA_OPT_O, NULL, ELFEDIT_CMDOA_F_INHERIT, 0, 0 }, { NULL } }; static elfedit_cmd_optarg_t arg_interp[] = { { MSG_ORIG(MSG_STR_NEWPATH), /* MSG_INTL(MSG_A1_INTERP_NEWPATH) */ ELFEDIT_I18NHDL(MSG_A1_INTERP_NEWPATH), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* phdr:delete */ static const char *name_delete[] = { MSG_ORIG(MSG_CMD_DELETE), NULL }; static elfedit_cmd_optarg_t arg_delete[] = { { MSG_ORIG(MSG_STR_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { MSG_ORIG(MSG_STR_COUNT), /* MSG_INTL(MSG_A2_DELETE_COUNT) */ ELFEDIT_I18NHDL(MSG_A2_DELETE_COUNT), ELFEDIT_CMDOA_F_OPT }, { NULL } }; /* phdr:move */ static const char *name_move[] = { MSG_ORIG(MSG_CMD_MOVE), NULL }; static elfedit_cmd_optarg_t arg_move[] = { { MSG_ORIG(MSG_STR_ELEMENT), /* MSG_INTL(MSG_A1_ELEMENT) */ ELFEDIT_I18NHDL(MSG_A1_ELEMENT), ELFEDIT_CMDOA_F_OPT }, { 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 } }; static elfedit_cmd_t cmds[] = { /* phdr:dump */ { cmd_dump, cpl_1starg_pt, name_dump, /* MSG_INTL(MSG_DESC_DUMP) */ ELFEDIT_I18NHDL(MSG_DESC_DUMP), /* MSG_INTL(MSG_HELP_DUMP) */ ELFEDIT_I18NHDL(MSG_HELP_DUMP), opt_minus_phndx, arg_dump }, /* phdr:p_type */ { cmd_p_type, cpl_p_type, name_p_type, /* MSG_INTL(MSG_DESC_P_TYPE) */ ELFEDIT_I18NHDL(MSG_DESC_P_TYPE), /* MSG_INTL(MSG_HELP_P_TYPE) */ ELFEDIT_I18NHDL(MSG_HELP_P_TYPE), opt_std, arg_p_type }, /* phdr:p_offset */ { cmd_p_offset, cpl_1starg_pt, name_p_offset, /* MSG_INTL(MSG_DESC_P_OFFSET) */ ELFEDIT_I18NHDL(MSG_DESC_P_OFFSET), /* MSG_INTL(MSG_HELP_P_OFFSET) */ ELFEDIT_I18NHDL(MSG_HELP_P_OFFSET), opt_std, arg_p_offset }, /* phdr:p_vaddr */ { cmd_p_vaddr, cpl_1starg_pt, name_p_vaddr, /* MSG_INTL(MSG_DESC_P_VADDR) */ ELFEDIT_I18NHDL(MSG_DESC_P_VADDR), /* MSG_INTL(MSG_HELP_P_VADDR) */ ELFEDIT_I18NHDL(MSG_HELP_P_VADDR), opt_std, arg_p_vaddr }, /* phdr:p_paddr */ { cmd_p_paddr, cpl_1starg_pt, name_p_paddr, /* MSG_INTL(MSG_DESC_P_PADDR) */ ELFEDIT_I18NHDL(MSG_DESC_P_PADDR), /* MSG_INTL(MSG_HELP_P_PADDR) */ ELFEDIT_I18NHDL(MSG_HELP_P_PADDR), opt_std, arg_p_paddr }, /* phdr:p_filesz */ { cmd_p_filesz, cpl_1starg_pt, name_p_filesz, /* MSG_INTL(MSG_DESC_P_FILESZ) */ ELFEDIT_I18NHDL(MSG_DESC_P_FILESZ), /* MSG_INTL(MSG_HELP_P_FILESZ) */ ELFEDIT_I18NHDL(MSG_HELP_P_FILESZ), opt_std, arg_p_filesz }, /* phdr:p_memsz */ { cmd_p_memsz, cpl_1starg_pt, name_p_memsz, /* MSG_INTL(MSG_DESC_P_MEMSZ) */ ELFEDIT_I18NHDL(MSG_DESC_P_MEMSZ), /* MSG_INTL(MSG_HELP_P_MEMSZ) */ ELFEDIT_I18NHDL(MSG_HELP_P_MEMSZ), opt_std, arg_p_memsz }, /* phdr:p_flags */ { cmd_p_flags, cpl_p_flags, name_p_flags, /* MSG_INTL(MSG_DESC_P_FLAGS) */ ELFEDIT_I18NHDL(MSG_DESC_P_FLAGS), /* MSG_INTL(MSG_HELP_P_FLAGS) */ ELFEDIT_I18NHDL(MSG_HELP_P_FLAGS), opt_p_flags, arg_p_flags }, /* phdr:p_align */ { cmd_p_align, cpl_1starg_pt, name_p_align, /* MSG_INTL(MSG_DESC_P_ALIGN) */ ELFEDIT_I18NHDL(MSG_DESC_P_ALIGN), /* MSG_INTL(MSG_HELP_P_ALIGN) */ ELFEDIT_I18NHDL(MSG_HELP_P_ALIGN), opt_std, arg_p_align }, /* phdr:interp */ { cmd_interp, NULL, name_interp, /* MSG_INTL(MSG_DESC_INTERP) */ ELFEDIT_I18NHDL(MSG_DESC_INTERP), /* MSG_INTL(MSG_HELP_INTERP) */ ELFEDIT_I18NHDL(MSG_HELP_INTERP), opt_interp, arg_interp }, /* phdr:delete */ { cmd_delete, cpl_1starg_pt, name_delete, /* MSG_INTL(MSG_DESC_DELETE) */ ELFEDIT_I18NHDL(MSG_DESC_DELETE), /* MSG_INTL(MSG_HELP_DELETE) */ ELFEDIT_I18NHDL(MSG_HELP_DELETE), opt_minus_phndx, arg_delete }, /* phdr:move */ { cmd_move, cpl_1starg_pt, name_move, /* MSG_INTL(MSG_DESC_MOVE) */ ELFEDIT_I18NHDL(MSG_DESC_MOVE), /* MSG_INTL(MSG_HELP_MOVE) */ ELFEDIT_I18NHDL(MSG_HELP_MOVE), opt_minus_phndx, arg_move }, { 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); }